@aetherwealth/mcp 0.2.15 → 0.2.16
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/dist/index.js +5 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -104,10 +104,10 @@ data:
|
|
|
104
104
|
`).trimEnd()}var PX=["Aether Wealth spans a trading journal, alerts, market data and indicators, a","macro calendar, charts, Pine Studio, and the Aether Intelligence assistant. Many","of these are callable over MCP; some are dashboard-only."].join(" ");function i5($={}){let v=$.hosted??!1,D=v?["Authentication is handled by the MCP connection itself — there are no sign-in tools to call. If calls fail with an authorization error, the user must re-authorize this connection in their MCP client settings."]:["Auth tools:"," • login — sign in. Opens the browser; the user authenticates and approves access. Call this first if a data tool reports the user is not signed in."," • logout — sign out on this device."],X=v?" 3. If it fails with an authorization error, tell the user to re-authorize the Aether Wealth connection in their MCP client, then retry.":' 3. If it reports "not signed in", call login, wait for the user to finish in the browser, then retry.';return[{name:"getting_started",description:"Brief the LLM on what the Aether Wealth MCP exposes and the sign-in-first flow.",handler:()=>[{role:"user",content:{type:"text",text:["You are connected to the Aether Wealth MCP — programmatic access to the user's trading journal, alerts, market data, indicators, and charts over an authenticated connection.","",...D,"","Context:"," • get_context — snapshot accounts, open trades, recent stats, and active alerts in one call. Call it first to ground your suggestions.","","Domains (partial — see the aetherwealth://capabilities resource for the full set):"," • Journal: list_trades, get_trade, list_accounts, create_account, create_trade, update_trade, close_trade, delete_trade, delete_trades, import_trades, trade_stats"," • Alerts: list_alerts, create_price_alert, create_trendline_alert, create_indicator_alert, list_indicator_alerts, update_indicator_alert, delete_indicator_alert, update_alert, delete_alert"," • Market data + indicators: get_candles, get_market_config, get_indicators"," • Macro: list_economic_calendar, get_macro_series, get_macro_indicator"," • FX macro context: get_fx_pair_context, get_fx_currency_context, get_fx_predictions, get_fx_risk_sentiment, get_fx_documents, get_fx_sync_state",...Y4?[]:[" • Charts: render_chart"]," • Broker execution: propose_trade, close_position, and modify_position create intents. Email/app modes need a user-provided code; auto mode needs no per-trade code. For push intents, do not ask for a code — use trade_intent_status to check the app approval outcome. disconnect_broker is the instant kill switch and requires no confirmation.","","Deletion rule:"," • delete_trade and delete_trades are permanent and only work on demo accounts. Live-account trade deletes are blocked by the backend.","","Recommended flow:"," 1. Call get_context to load live state."," 2. Try the data tool the user asked for (e.g. list_trades).",X].join(`
|
|
105
105
|
`)}}]},{name:"dashboard_context",description:"Brief the LLM on the full Aether Wealth surface, grounding in live state, and when to suggest the dashboard.",handler:()=>[{role:"user",content:{type:"text",text:["You are connected to Aether Wealth, a multi-market trading workspace.",PX,"","Before you advise, call get_context to load the user's live state (accounts, open trades, recent stats, active alerts) so your suggestions fit what they are actually doing. The aetherwealth://capabilities resource, if your client attaches it, is the full surface map — which actions are MCP-callable vs dashboard-only.","","When the user needs something that is dashboard-only (the full charting workspace and saved layouts, the Pine editor, the full journal UI, the Aether Intelligence assistant),",`tell them to open the dashboard at ${f4} rather than attempting it over MCP.`].join(`
|
|
106
106
|
`)}}]}]}function y5(){return[{uri:"aetherwealth://capabilities",name:"Aether Wealth capabilities",description:"What Aether Wealth can do across all surfaces, and which actions are MCP-callable vs dashboard-only. Attach for product context.",mimeType:"text/markdown",handler:async()=>({text:SZ()})}]}function vR($){return{...$.broker===void 0?{}:{broker:$.broker},...$.accountType===void 0?{}:{accountType:$.accountType},...$.initialBalance===void 0?{}:{initialBalance:$.initialBalance},...$.currency===void 0?{}:{currency:$.currency},...$.notes===void 0?{}:{notes:$.notes},...$.defaultRiskAmount===void 0?{}:{defaultRiskAmount:$.defaultRiskAmount}}}function UR($){return{name:$.name,...vR($)}}function kZ($){return $.trading.accounts.list.query()}function EZ($,v){return $.trading.accounts.create.mutate(UR(v))}function n5($){return{triggerType:$.triggerType??"close",isPersistent:$.isPersistent??!1,notifyEmail:$.notifyEmail??!0,notifyPush:$.notifyPush??!0,...$.message===void 0?{}:{message:$.message},...$.expiresAt===void 0?{}:{expiresAt:$.expiresAt}}}function TZ($){return{...$.includeArchived===void 0?{}:{includeArchived:$.includeArchived},...$.pair===void 0?{}:{pair:$.pair}}}function DR($){return{id:$.id,...$.price===void 0?{}:{price:$.price},...$.price1===void 0?{}:{price1:$.price1},...$.price2===void 0?{}:{price2:$.price2},...$.time1===void 0?{}:{time1:$.time1},...$.time2===void 0?{}:{time2:$.time2},...$.condition===void 0?{}:{condition:$.condition},...$.triggerType===void 0?{}:{triggerType:$.triggerType},...$.timeframe===void 0?{}:{timeframe:$.timeframe},...$.isActive===void 0?{}:{isActive:$.isActive},...$.isArchived===void 0?{}:{isArchived:$.isArchived},...$.isPersistent===void 0?{}:{isPersistent:$.isPersistent},...$.notifyEmail===void 0?{}:{notifyEmail:$.notifyEmail},...$.notifyPush===void 0?{}:{notifyPush:$.notifyPush},...$.message===void 0?{}:{message:$.message},...$.expiresAt===void 0?{}:{expiresAt:$.expiresAt}}}function XR($){return{id:$.id,...$.condition===void 0?{}:{condition:$.condition},...$.dedupMode===void 0?{}:{dedupMode:$.dedupMode},...$.triggerType===void 0?{}:{triggerType:$.triggerType},...$.isActive===void 0?{}:{isActive:$.isActive},...$.isArchived===void 0?{}:{isArchived:$.isArchived},...$.isPersistent===void 0?{}:{isPersistent:$.isPersistent},...$.notifyEmail===void 0?{}:{notifyEmail:$.notifyEmail},...$.notifyPush===void 0?{}:{notifyPush:$.notifyPush},...$.message===void 0?{}:{message:$.message},...$.expiresAt===void 0?{}:{expiresAt:$.expiresAt}}}function RZ($,v){return $.alerts.list.query(TZ(v))}function gZ($,v){let D={pair:v.pair,timeframe:v.timeframe,price:v.price,condition:v.condition,...n5(v)};return $.alerts.createPrice.mutate(D)}function CZ($,v){let D={pair:v.pair,timeframe:v.timeframe,price1:v.price1,time1:v.time1,price2:v.price2,time2:v.time2,condition:v.condition,...n5(v)};return $.alerts.createTrendline.mutate(D)}function uZ($,v){return $.alerts.update.mutate(DR(v))}function xZ($,v){return $.alerts.delete.mutate({id:v.id})}function fZ($,v){let D=TZ(v);return $.alerts.indicator.list.query(D)}function _Z($,v){let D={pair:v.pair,timeframe:v.timeframe,indicatorType:v.indicatorType,indicatorParams:v.indicatorParams,outputSeries:v.outputSeries,condition:v.condition,dedupMode:v.dedupMode??"edge",...n5(v)};return $.alerts.indicator.create.mutate(D)}function iZ($,v){return $.alerts.indicator.update.mutate(XR(v))}function yZ($,v){return $.alerts.indicator.delete.mutate({id:v.id})}function GR($){if($.provider==="mt5_executor")return{accountId:$.accountId,provider:$.provider,baseUrl:$.baseUrl,providerAccountRef:$.providerAccountRef,providerToken:$.providerToken,...$.approvalMode===void 0?{}:{approvalMode:$.approvalMode}};return{accountId:$.accountId,region:$.region,providerAccountRef:$.providerAccountRef,providerToken:$.providerToken,...$.provider===void 0?{}:{provider:$.provider},...$.approvalMode===void 0?{}:{approvalMode:$.approvalMode}}}function bX($){return{accountId:$.accountId}}function JR($){return{accountId:$.accountId,symbol:$.symbol}}function QR($){return{accountId:$.accountId,approvalMode:$.approvalMode,...$.totpCode===void 0?{}:{totpCode:$.totpCode}}}function nZ($){return{accountId:$.accountId,symbol:$.symbol,direction:$.direction,stopLoss:$.stopLoss,...$.orderType===void 0?{}:{orderType:$.orderType},...$.entryPrice===void 0?{}:{entryPrice:$.entryPrice},...$.takeProfit===void 0?{}:{takeProfit:$.takeProfit},...$.riskPercent===void 0?{}:{riskPercent:$.riskPercent},...$.riskAmount===void 0?{}:{riskAmount:$.riskAmount}}}function WR($){return{...nZ($),idempotencyKey:$.idempotencyKey}}function zR($){return{intentId:$.intentId,...$.code===void 0?{}:{code:$.code}}}function YR($){return{intentId:$.intentId}}function IR($){return{intentId:$.intentId}}function lZ($){return{accountId:$.accountId,positionId:$.positionId,...$.volume===void 0?{}:{volume:$.volume}}}function VR($){return{...lZ($),idempotencyKey:$.idempotencyKey}}function hZ($){return{accountId:$.accountId,positionId:$.positionId,...$.stopLoss===void 0?{}:{stopLoss:$.stopLoss},...$.takeProfit===void 0?{}:{takeProfit:$.takeProfit}}}function KR($){return{...hZ($),idempotencyKey:$.idempotencyKey}}function mZ($,v){return $.execution.connect.mutate(GR(v))}function cZ($,v){return $.execution.status.query(bX(v))}function dZ($,v){return $.execution.disconnect.mutate(bX(v))}function rZ($,v){return $.execution.setApprovalMode.mutate(QR(v))}function pZ($,v){return $.execution.account.query(bX(v))}function tZ($,v){return $.execution.positions.query(bX(v))}function oZ($,v){return $.execution.quote.query(JR(v))}function aZ($,v){return $.execution.propose.mutate(nZ(v))}function eZ($,v){return $.execution.submitTrade.mutate(WR(v))}function sZ($,v){return $.execution.execute.mutate(zR(v))}function $O($,v){return $.execution.cancel.mutate(YR(v))}function vO($,v){return $.execution.intentStatus.query(IR(v))}function UO($,v){return $.execution.closePosition.mutate(lZ(v))}function DO($,v){return $.execution.submitClose.mutate(VR(v))}function XO($,v){return $.execution.modifyPosition.mutate(hZ(v))}function GO($,v){return $.execution.submitModify.mutate(KR(v))}function LR($){return{pair:$.pair,interval:$.interval,indicators:$.indicators.map((v)=>({name:v.name,...v.params===void 0?{}:{params:v.params}})),...$.outputsize===void 0?{}:{outputsize:$.outputsize}}}function JO($,v){return $.technicalIndicators.get.query(LR(v))}var QO=["3m","6m","12m","24m","all"];function BR($){return{...$.currency===void 0?{}:{currency:$.currency},...$.from===void 0?{}:{from:$.from},...$.to===void 0?{}:{to:$.to},...$.indicator===void 0?{}:{indicator:$.indicator},...$.impactMin===void 0?{}:{impactMin:$.impactMin},...$.view===void 0?{}:{view:$.view}}}function NR($){return{currency:$.currency,indicator:$.indicator,...$.from===void 0?{}:{from:$.from},...$.to===void 0?{}:{to:$.to},...$.limit===void 0?{}:{limit:$.limit}}}function ZR($){return{currency:$.currency,indicator:$.indicator,...$.range===void 0?{}:{range:$.range}}}function OR($){return{pair:$.pair,timeframe:$.timeframe,...$.limit===void 0?{}:{limit:$.limit},...$.offset===void 0?{}:{offset:$.offset},...$.cursor===void 0?{}:{cursor:$.cursor}}}function HR($){return{...OR($),...$.sourceId===void 0?{}:{sourceId:$.sourceId}}}function qX($,v){return $.marketData.mcpCandles.query(HR(v))}function WO($,v){return $.marketData.listCalendar.query(BR(v))}function zO($,v){return $.marketData.listIndicatorSeries.query(NR(v))}function YO($,v){return $.macroIndicator.list.query(ZR(v))}function IO($){return $.marketData.getConfig.query()}function FR($){return{...$.accountId===void 0?{}:{accountId:$.accountId},...$.pair===void 0?{}:{pair:$.pair},...$.from===void 0?{}:{from:$.from},...$.to===void 0?{}:{to:$.to}}}function VO($,v){return $.trading.analytics.stats.query(FR(v))}function jR($){return{...$.accountId===void 0?{}:{accountId:$.accountId},...$.status===void 0||$.status==="ALL"?{}:{status:$.status},...$.pair===void 0?{}:{pair:$.pair},...$.from===void 0?{}:{from:$.from},...$.to===void 0?{}:{to:$.to},...$.page===void 0?{}:{page:$.page},...$.limit===void 0?{}:{limit:$.limit}}}function KO($){return{...$.stopLoss===void 0?{}:{stopLoss:$.stopLoss},...$.takeProfit===void 0?{}:{takeProfit:$.takeProfit},...$.lotSize===void 0?{}:{lotSize:$.lotSize},...$.notes===void 0?{}:{notes:$.notes},...$.timeframe===void 0?{}:{timeframe:$.timeframe},...$.riskPercent===void 0?{}:{riskPercent:$.riskPercent},...$.tags===void 0?{}:{tags:$.tags}}}function AR($){return{accountId:$.accountId,pair:$.pair,direction:$.direction,entryPrice:$.entryPrice,entryTime:$.entryTime,...KO($)}}function wR($){return{id:$.id,...$.pair===void 0?{}:{pair:$.pair},...$.direction===void 0?{}:{direction:$.direction},...$.entryPrice===void 0?{}:{entryPrice:$.entryPrice},...$.entryTime===void 0?{}:{entryTime:$.entryTime},...KO($)}}function MR($){return{id:$.id,exitPrice:$.exitPrice,exitTime:$.exitTime,...$.fees===void 0?{}:{fees:$.fees},...$.swap===void 0?{}:{swap:$.swap},...$.notes===void 0?{}:{notes:$.notes}}}function PR($){return{accountId:$.accountId,status:$.status}}function bR($){return{pair:$.pair,direction:$.direction,entryPrice:$.entryPrice,lotSize:$.lotSize,entryTime:$.entryTime,...$.exitPrice===void 0?{}:{exitPrice:$.exitPrice},...$.stopLoss===void 0?{}:{stopLoss:$.stopLoss},...$.takeProfit===void 0?{}:{takeProfit:$.takeProfit},...$.exitTime===void 0?{}:{exitTime:$.exitTime},...$.notes===void 0?{}:{notes:$.notes},...$.timeframe===void 0?{}:{timeframe:$.timeframe},...$.emotionalTag===void 0?{}:{emotionalTag:$.emotionalTag},...$.fees===void 0?{}:{fees:$.fees},...$.swap===void 0?{}:{swap:$.swap},...$.reportedPnl===void 0?{}:{reportedPnl:$.reportedPnl},...$.tags===void 0?{}:{tags:$.tags}}}function qR($){return{accountId:$.accountId,trades:$.trades.map(bR)}}function LO($,v){return $.trading.trades.list.query(jR(v))}function BO($,v){return $.trading.trades.get.query({id:v.id})}function NO($,v){return $.trading.trades.create.mutate(AR(v))}function ZO($,v){return $.trading.trades.update.mutate(wR(v))}function OO($,v){return $.trading.trades.close.mutate(MR(v))}function HO($,v){return $.trading.trades.delete.mutate({id:v.id})}function FO($,v){return $.trading.trades.deleteMany.mutate(PR(v))}function jO($,v){return $.trading.trades.importMany.mutate(qR(v))}function E($){return $}var l5='Not signed in to Aether Wealth. Call the "login" tool to authenticate, then retry.';async function T($){if(!await $.session.getAccessToken())throw Error(l5)}var AO={type:"integer",minimum:1,maximum:200,description:"Max rows to return"},wO={type:"integer",minimum:1,description:"Page number (1-indexed)"};function SX($,v){if(v===null||v===void 0){if($.required&&$.required.length>0)return{ok:!1,reason:`missing required: ${$.required.join(", ")}`};return{ok:!0,args:{}}}if(typeof v!=="object"||Array.isArray(v))return{ok:!1,reason:"args must be an object"};let D=v,X=SR($,D)??kR($,D);return X?{ok:!1,reason:X}:{ok:!0,args:D}}function SR($,v){for(let D of $.required??[])if(!(D in v)||v[D]===void 0||v[D]===null)return`missing required: ${D}`;return null}function kR($,v){if($.additionalProperties!==!1)return null;for(let D of Object.keys(v))if(!(D in $.properties))return`unknown property: ${D}`;return null}var h5={type:"string",enum:["above","below","crosses"]},w0={triggerType:{type:"string",enum:["close","wick"],description:"default: close"},isPersistent:{type:"boolean",description:"Re-arm after firing. default: false"},notifyEmail:{type:"boolean",description:"default: true"},notifyPush:{type:"boolean",description:"default: true"},message:{type:"string"},expiresAt:{type:"string",description:"ISO 8601 datetime"}},kX=[E({name:"list_alerts",description:"List your price + trendline alerts. Optionally include archived or filter by pair. Requires sign-in.",inputSchema:{type:"object",properties:{includeArchived:{type:"boolean"},pair:{type:"string"}},additionalProperties:!1},handler:async($,v)=>{return await T(v),RZ(v.trpc,$)}}),E({name:"create_price_alert",description:"Create a price-level alert (fires when price is above/below/crosses a level). Requires sign-in.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},price:{type:"number"},condition:h5,...w0},required:["pair","timeframe","price","condition"],additionalProperties:!1},handler:async($,v)=>{return await T(v),gZ(v.trpc,$)}}),E({name:"create_trendline_alert",description:"Create a trendline alert (fires when price crosses a line defined by two points). Requires sign-in.",inputSchema:{type:"object",properties:{pair:{type:"string"},timeframe:{type:"string"},price1:{type:"number"},time1:{type:"string",description:"ISO 8601 datetime of point 1"},price2:{type:"number"},time2:{type:"string",description:"ISO 8601 datetime of point 2"},condition:h5,...w0},required:["pair","timeframe","price1","time1","price2","time2","condition"],additionalProperties:!1},handler:async($,v)=>{return await T(v),CZ(v.trpc,$)}}),E({name:"update_alert",description:"Update a price/trendline alert (e.g. change level, archive, mute). Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},price:{type:"number"},price1:{type:"number"},price2:{type:"number"},time1:{type:"string"},time2:{type:"string"},condition:h5,timeframe:{type:"string"},isActive:{type:"boolean"},isArchived:{type:"boolean"},...w0},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),uZ(v.trpc,$)}}),E({name:"delete_alert",description:"Delete a price/trendline alert by id. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),xZ(v.trpc,$)}}),E({name:"list_indicator_alerts",description:"List your indicator-based alerts. Optionally include archived or filter by pair. Requires sign-in.",inputSchema:{type:"object",properties:{includeArchived:{type:"boolean"},pair:{type:"string"}},additionalProperties:!1},handler:async($,v)=>{return await T(v),fZ(v.trpc,$)}}),E({name:"create_indicator_alert",description:"Create an indicator alert (fires on an indicator output condition, e.g. RSI crosses 70). `condition` is { op, threshold } or { op, other }. Requires sign-in.",inputSchema:{type:"object",properties:{pair:{type:"string"},timeframe:{type:"string"},indicatorType:{type:"string",description:"e.g. rsi, ema, macd"},indicatorParams:{type:"object",description:"Indicator settings, e.g. { length: 14 }"},outputSeries:{type:"string",description:"Which output series to watch"},condition:{type:"object",description:"{ op: gt|gte|lt|lte|eq|crosses_above|crosses_below, threshold } or { op: *_series, other }"},dedupMode:{type:"string",enum:["edge","continuous"],description:"default: edge"},...w0},required:["pair","timeframe","indicatorType","indicatorParams","outputSeries","condition"],additionalProperties:!1},handler:async($,v)=>{return await T(v),_Z(v.trpc,$)}}),E({name:"update_indicator_alert",description:"Update an indicator alert (condition, dedup, archive, mute). Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},condition:{type:"object"},dedupMode:{type:"string",enum:["edge","continuous"]},isActive:{type:"boolean"},isArchived:{type:"boolean"},...w0},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),iZ(v.trpc,$)}}),E({name:"delete_indicator_alert",description:"Delete an indicator alert by id. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),yZ(v.trpc,$)}})];var EX=[E({name:"login",description:"Sign in to Aether Wealth. Opens your browser to authenticate; once you approve, the MCP stores a per-user token and the data tools become available.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await v.session.login(),{status:"signed_in",message:"Signed in to Aether Wealth."}}}),E({name:"logout",description:"Sign out of Aether Wealth on this device. Clears the stored access token.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await v.session.logout(),{status:"signed_out",message:"Signed out on this device."}}})];var B$=($,v,D,X)=>{return{id:$,displayName:`${$.slice(0,3)}/${$.slice(3)}`,kind:"forex",exchange:"FX",region:"GLOBAL",category:D,pipSize:X?.pipSize??0.0001,priceDecimals:X?.priceDecimals??5,contractSize:1e5,providers:{twelvedata:v}}},ER=[B$("AUDUSD","AUD/USD","Forex Majors"),B$("EURUSD","EUR/USD","Forex Majors"),B$("GBPUSD","GBP/USD","Forex Majors"),B$("USDCAD","USD/CAD","Forex Majors"),B$("USDCHF","USD/CHF","Forex Majors"),B$("USDJPY","USD/JPY","Forex Majors",{pipSize:0.01,priceDecimals:3}),B$("AUDCAD","AUD/CAD","Forex Minors"),B$("AUDCHF","AUD/CHF","Forex Minors"),B$("AUDJPY","AUD/JPY","Forex Minors",{pipSize:0.01,priceDecimals:3}),B$("AUDNZD","AUD/NZD","Forex Minors"),B$("EURAUD","EUR/AUD","Forex Minors"),B$("EURCAD","EUR/CAD","Forex Minors"),B$("EURCHF","EUR/CHF","Forex Minors"),B$("EURGBP","EUR/GBP","Forex Minors"),B$("EURJPY","EUR/JPY","Forex Minors",{pipSize:0.01,priceDecimals:3}),B$("EURNZD","EUR/NZD","Forex Minors"),B$("GBPAUD","GBP/AUD","Forex Minors"),B$("GBPCAD","GBP/CAD","Forex Minors"),B$("GBPCHF","GBP/CHF","Forex Minors"),B$("GBPJPY","GBP/JPY","Forex Minors",{pipSize:0.01,priceDecimals:3}),B$("GBPNZD","GBP/NZD","Forex Minors")],TR=[{id:"XAUUSD",displayName:"Gold",kind:"metal",exchange:"OTC",region:"GLOBAL",category:"Metals",pipSize:0.01,priceDecimals:2,contractSize:100,providers:{twelvedata:"XAU/USD",mt5:"XAUUSD"}},{id:"XAGUSD",displayName:"Silver",kind:"metal",exchange:"OTC",region:"GLOBAL",category:"Metals",pipSize:0.001,priceDecimals:3,contractSize:5000,providers:{twelvedata:"XAG/USD"}}];function i$($,v,D=!1){return{id:$,displayName:`${$.slice(0,-3)}/USD`,kind:"crypto",exchange:"CRYPTO",region:"GLOBAL",category:"Crypto",pipSize:D?0.0001:0.01,priceDecimals:D?5:2,contractSize:1,providers:{twelvedata:v}}}var RR=[i$("BTCUSD","BTC/USD"),i$("ETHUSD","ETH/USD"),i$("SOLUSD","SOL/USD"),i$("BNBUSD","BNB/USD"),i$("AVAXUSD","AVAX/USD"),i$("LINKUSD","LINK/USD"),i$("LTCUSD","LTC/USD"),i$("BCHUSD","BCH/USD"),i$("ATOMUSD","ATOM/USD"),i$("DOTUSD","DOT/USD"),i$("XRPUSD","XRP/USD",!0),i$("ADAUSD","ADA/USD",!0),i$("DOGEUSD","DOGE/USD",!0),i$("TRXUSD","TRX/USD",!0),i$("POLUSD","POL/USD",!0)],gR=[{id:"NAS100",displayName:"Nasdaq 100",kind:"index",exchange:"NASDAQ",region:"US",category:"Indices",pipSize:0.01,priceDecimals:2,contractSize:20,providers:{twelvedata:"QQQ"}}],CR=[{id:"USOIL",displayName:"WTI Crude",kind:"commodity",exchange:"NYMEX",region:"US",category:"Commodities",pipSize:0.01,priceDecimals:2,contractSize:1000,providers:{twelvedata:"WTI/USD"}}];function T4($,v,D){return{id:$,displayName:D,kind:"stock",exchange:"NSE",region:"IN",category:"Indian Stocks",pipSize:0.05,priceDecimals:2,contractSize:1,providers:{twelvedata:v}}}var uR=[{id:"NIFTYBEES",displayName:"NIFTY 50 ETF",kind:"index",exchange:"BSE",region:"IN",category:"Indian Indices",pipSize:0.01,priceDecimals:2,contractSize:1,providers:{twelvedata:"NIFTYBEES:BSE"}},{id:"BANKBEES",displayName:"BANK NIFTY ETF",kind:"index",exchange:"BSE",region:"IN",category:"Indian Indices",pipSize:0.01,priceDecimals:2,contractSize:1,providers:{twelvedata:"BANKBEES:BSE"}},T4("RELIANCE","RELIANCE:NSE","Reliance Industries"),T4("TCS","TCS:NSE","Tata Consultancy Services"),T4("HDFCBANK","HDFCBANK:NSE","HDFC Bank"),T4("INFY","INFY:NSE","Infosys"),T4("ICICIBANK","ICICIBANK:NSE","ICICI Bank"),T4("BHARTIARTL","BHARTIARTL:NSE","Bharti Airtel"),T4("LT","LT:NSE","Larsen & Toubro"),T4("SBIN","SBIN:NSE","State Bank of India"),T4("AXISBANK","AXISBANK:NSE","Axis Bank"),T4("KOTAKBANK","KOTAKBANK:NSE","Kotak Mahindra Bank")],TX=Object.freeze([...ER,...TR,...RR,...gR,...CR,...uR]);class M0{byKey;instruments;providerNames;constructor($){this.instruments=Object.freeze([...$]);let v=new Map,D=new Set;for(let X of $){if(m5(v,X.id,X,"id"),X.displayName!==X.id)m5(v,X.displayName,X,"displayName");for(let[U,G]of Object.entries(X.providers))if(D.add(U),m5(v,MO(U,G),X,"provider"),!v.has(G))v.set(G,X)}this.byKey=v,this.providerNames=D}resolve($){let v=this.byKey.get($);if(v)return v;let D=$.toUpperCase();if(D!==$){let U=this.byKey.get(D);if(U)return U}let X=$.toLowerCase();if(X!==$)return this.byKey.get(X)??null;return null}require($){let v=this.resolve($);if(!v)throw Error(`Unknown instrument: ${JSON.stringify($)}. Add it to INSTRUMENTS env var or check the key you passed in.`);return v}resolveByProvider($,v){return this.byKey.get(MO($,v))??null}list(){return this.instruments}byKind($){return this.instruments.filter((v)=>v.kind===$)}byExchange($){return this.instruments.filter((v)=>v.exchange===$)}byRegion($){return this.instruments.filter((v)=>v.region===$)}byCategory($){return this.instruments.filter((v)=>v.category===$)}groupByCategory(){let $=new Map;for(let v of this.instruments){let D=$.get(v.category);if(D)D.push(v);else $.set(v.category,[v])}return Array.from($.entries()).map(([v,D])=>({category:v,instruments:D}))}has($){return this.byKey.has($)}stats(){return{instrumentCount:this.instruments.length,providerCount:this.providerNames.size,providers:Array.from(this.providerNames).sort()}}}function MO($,v){return`${$}:${v}`}function m5($,v,D,X){if(X==="provider"){if($.has(v))throw Error(`Duplicate provider symbol ${JSON.stringify(v)} declared by multiple instruments in INSTRUMENTS`)}else if($.has(v))throw Error(`Duplicate instrument ${X} ${JSON.stringify(v)} in INSTRUMENTS (already mapped to ${$.get(v)?.id}, now also to ${D.id})`);$.set(v,D)}var fR=["forex","metal","crypto","index","commodity","stock","bond"],_R=l.object({kind:l.enum(fR),displayName:l.string().min(1).optional(),exchange:l.string().min(1).optional(),region:l.string().min(1).optional(),category:l.string().min(1).optional(),pipSize:l.number().positive().optional(),priceDecimals:l.number().int().min(0).max(12).optional(),contractSize:l.number().positive().optional(),providers:l.record(l.string().min(1),l.string().min(1))}),hp=l.record(l.string().min(1),_R).refine(($)=>Object.keys($).length>0,{message:"INSTRUMENTS must contain at least one entry"});var iR=Object.freeze({AUDUSD:{quote:"https://logo.twelvedata.com/forex/usd.png",base:"https://logo.twelvedata.com/forex/aud.png"},EURUSD:{base:"https://logo.twelvedata.com/forex/eur.png",quote:"https://logo.twelvedata.com/forex/usd.png"},GBPUSD:{quote:"https://logo.twelvedata.com/forex/usd.png",base:"https://logo.twelvedata.com/forex/gbp.png"},USDCAD:{base:"https://logo.twelvedata.com/forex/usd.png",quote:"https://logo.twelvedata.com/forex/cad.png"},USDCHF:{base:"https://logo.twelvedata.com/forex/usd.png",quote:"https://logo.twelvedata.com/forex/chf.png"},USDJPY:{base:"https://logo.twelvedata.com/forex/usd.png",quote:"https://logo.twelvedata.com/forex/jpy.png"},AUDCAD:{base:"https://logo.twelvedata.com/forex/aud.png",quote:"https://logo.twelvedata.com/forex/cad.png"},AUDCHF:{base:"https://logo.twelvedata.com/forex/aud.png",quote:"https://logo.twelvedata.com/forex/chf.png"},AUDJPY:{base:"https://logo.twelvedata.com/forex/aud.png",quote:"https://logo.twelvedata.com/forex/jpy.png"},AUDNZD:{base:"https://logo.twelvedata.com/forex/aud.png",quote:"https://logo.twelvedata.com/forex/nzd.png"},EURAUD:{base:"https://logo.twelvedata.com/forex/eur.png",quote:"https://logo.twelvedata.com/forex/aud.png"},EURCAD:{base:"https://logo.twelvedata.com/forex/eur.png",quote:"https://logo.twelvedata.com/forex/cad.png"},EURCHF:{base:"https://logo.twelvedata.com/forex/eur.png",quote:"https://logo.twelvedata.com/forex/chf.png"},EURGBP:{quote:"https://logo.twelvedata.com/forex/gbp.png",base:"https://logo.twelvedata.com/forex/eur.png"},EURJPY:{base:"https://logo.twelvedata.com/forex/eur.png",quote:"https://logo.twelvedata.com/forex/jpy.png"},EURNZD:{base:"https://logo.twelvedata.com/forex/eur.png",quote:"https://logo.twelvedata.com/forex/nzd.png"},GBPAUD:{base:"https://logo.twelvedata.com/forex/gbp.png",quote:"https://logo.twelvedata.com/forex/aud.png"},GBPCAD:{base:"https://logo.twelvedata.com/forex/gbp.png",quote:"https://logo.twelvedata.com/forex/cad.png"},GBPCHF:{quote:"https://logo.twelvedata.com/forex/chf.png",base:"https://logo.twelvedata.com/forex/gbp.png"},GBPJPY:{base:"https://logo.twelvedata.com/forex/gbp.png",quote:"https://logo.twelvedata.com/forex/jpy.png"},GBPNZD:{base:"https://logo.twelvedata.com/forex/gbp.png",quote:"https://logo.twelvedata.com/forex/nzd.png"},BTCUSD:{base:"https://logo.twelvedata.com/crypto/btc.png",quote:"https://logo.twelvedata.com/crypto/usd.png"},ETHUSD:{quote:"https://logo.twelvedata.com/crypto/usd.png",base:"https://logo.twelvedata.com/crypto/eth.png"},SOLUSD:{quote:"https://logo.twelvedata.com/crypto/usd.png",base:"https://logo.twelvedata.com/crypto/sol.png"},BNBUSD:{quote:"https://logo.twelvedata.com/crypto/usd.png",base:"https://logo.twelvedata.com/crypto/bnb.png"},AVAXUSD:{url:"https://assets.coingecko.com/coins/images/12559/large/Avalanche_Circle_RedWhite_Trans.png"},LINKUSD:{base:"https://logo.twelvedata.com/crypto/link.png",quote:"https://logo.twelvedata.com/crypto/usd.png"},LTCUSD:{base:"https://logo.twelvedata.com/crypto/ltc.png",quote:"https://logo.twelvedata.com/crypto/usd.png"},BCHUSD:{base:"https://logo.twelvedata.com/crypto/bch.png",quote:"https://logo.twelvedata.com/crypto/usd.png"},ATOMUSD:{quote:"https://logo.twelvedata.com/crypto/usd.png",base:"https://logo.twelvedata.com/crypto/atom.png"},TONUSD:{url:"https://assets.coingecko.com/coins/images/17980/large/ton_symbol.png"},DOTUSD:{quote:"https://logo.twelvedata.com/crypto/usd.png",base:"https://logo.twelvedata.com/crypto/dot.png"},XRPUSD:{base:"https://logo.twelvedata.com/crypto/xrp.png",quote:"https://logo.twelvedata.com/crypto/usd.png"},ADAUSD:{quote:"https://logo.twelvedata.com/crypto/usd.png",base:"https://logo.twelvedata.com/crypto/ada.png"},DOGEUSD:{base:"https://logo.twelvedata.com/crypto/doge.png",quote:"https://logo.twelvedata.com/crypto/usd.png"},TRXUSD:{base:"https://logo.twelvedata.com/crypto/trx.png",quote:"https://logo.twelvedata.com/crypto/usd.png"},POLUSD:{url:"https://assets.coingecko.com/coins/images/4713/large/polygon.png"},RELIANCE:{url:"https://api.twelvedata.com/logo/ril.com"},TCS:{url:"https://api.twelvedata.com/logo/tcs.com"},HDFCBANK:{url:"https://api.twelvedata.com/logo/hdfcbank.com"},INFY:{url:"https://api.twelvedata.com/logo/infosys.com"},ICICIBANK:{url:"https://api.twelvedata.com/logo/icicibank.com"},BHARTIARTL:{url:"https://api.twelvedata.com/logo/airtel.in"},LT:{url:"https://api.twelvedata.com/logo/larsentoubro.com"},SBIN:{url:"https://api.twelvedata.com/logo/sbi.co.in"},AXISBANK:{url:"https://api.twelvedata.com/logo/axisbank.com"},KOTAKBANK:{url:"https://api.twelvedata.com/logo/kotak.com"}});function c5($,v){return Number(v.toFixed($.priceDecimals))}var R$="-apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto, sans-serif",d5=11,r5=15,p5=11,t5=13,PO=12,RX=17,bO=26,qO=0.85,SO=20,kO=8,EO=64,TO=26,f1=18,gX=16,RO=20,gO=300,o5=120,CO=1280,uO=720,xO=2,fO=!0,_O=320,iO=2560,yO=240,nO=1440,lO=1,hO=3,CX={dark:{background:"#0F0F0F",text:"#D9D9D9",mutedText:"#8A8D96",grid:"rgba(242, 242, 242, 0.06)",axisBorder:"#2B2B43",upCandle:"#10b981",downCandle:"#ef4444",markerText:"#F2F2F2",headerText:"#E8E8EA",badgeBg:"rgba(255, 255, 255, 0.08)",badgeText:"#A6AAB4",watermarkText:"#D9D9D9",tagText:"#FFFFFF",entryTag:"#363C4E"},light:{background:"#FFFFFF",text:"#191919",mutedText:"#6A6E78",grid:"rgba(0, 0, 0, 0.06)",axisBorder:"#E0E3EB",upCandle:"#10b981",downCandle:"#ef4444",markerText:"#191919",headerText:"#131722",badgeBg:"rgba(0, 0, 0, 0.06)",badgeText:"#5F6470",watermarkText:"#191919",tagText:"#FFFFFF",entryTag:"#68707F"}},a5=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function mO($,v){if(!Number.isFinite(v))return"0.00000";if(!$)return v.toFixed(5);return c5($,v).toFixed($.priceDecimals)}function cO($,v,D){let X=new Date(v*1000);if(hR($)){if(D===void 0||rR(D,v))return cR(X);return mR(X)}return dR(X)}function hR($){let v=$.trim();if(v.length===0)return!0;if(/^\d*[DWM]$/.test(v))return!1;if(/(d|w|mo|month)/i.test(v))return!1;return!0}function P0($,v,D){let U=`${$>0?"+":""}${$.toFixed(D)}`;if(v===null||!Number.isFinite(v))return U;let G=v>0?"+":"";return`${U} (${G}${v.toFixed(2)}%)`}function mR($){return`${uX($.getUTCHours())}:${uX($.getUTCMinutes())}`}function cR($){return`${uX($.getUTCDate())} ${a5[$.getUTCMonth()]}`}function dR($){return`${uX($.getUTCDate())} ${a5[$.getUTCMonth()]} '${String($.getUTCFullYear()).slice(-2)}`}function rR($,v){let D=new Date($*1000),X=new Date(v*1000);return D.getUTCFullYear()!==X.getUTCFullYear()||D.getUTCMonth()!==X.getUTCMonth()||D.getUTCDate()!==X.getUTCDate()}function uX($){return String($).padStart(2,"0")}var dO="data:image/png;base64,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";function rO($){return $.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function I$($){return`<line${i1($)} />`}function F$($){return`<rect${i1($)} />`}function p$($,v){return`<text${i1(v)}>${rO($)}</text>`}function pO($){return`<image${i1($)} />`}function tO($,v){let D=$.map((X)=>`${e5(X.x)},${e5(X.y)}`).join(" ");return`<polygon${i1({...v,points:D})} />`}function o($,v={}){if($.length===0)return"";return`<g${i1(v)}>${$}</g>`}function oO($,v,D){return[`<svg xmlns="http://www.w3.org/2000/svg" width="${$}" height="${v}" viewBox="0 0 ${$} ${v}" fill="none">`,D,"</svg>"].join("")}function _1($,v=11){return Math.max(10,Math.round($.length*v*0.58))}function i1($){let v=Object.entries($).filter(([,D])=>D!==void 0&&D!==!1).map(([D,X])=>{if(X===!0)return`${D}="true"`;if(typeof X==="number")return`${D}="${e5(X)}"`;if(typeof X==="string")return`${D}="${rO(X)}"`;return""}).filter((D)=>D.length>0).join(" ");return v.length>0?` ${v}`:""}function e5($){let v=Math.round($*100)/100;return Number.isInteger(v)?String(v):v.toFixed(2)}var xX=16,pR=9,tR=12,oR=8,aR=8;function aO($,v){let D=[],X=$.plotLeft,U=$.instrument?.displayName??$.pair;D.push(p$(U,{x:X,y:xX,fill:v.headerText,"font-size":r5,"font-weight":700,"letter-spacing":0.2,"font-family":R$})),X+=_1(U,r5)+pR;let G=$.timeframe,J=_1(G,p5)+aR*2,Q=xX-RX+4;D.push(F$({x:X,y:Q,width:J,height:RX,rx:4,fill:v.badgeBg})),D.push(p$(G,{x:X+J/2,y:Q+RX/2+0.5,fill:v.badgeText,"font-size":p5,"font-weight":600,"text-anchor":"middle","dominant-baseline":"middle","font-family":R$})),X+=J+tR;let W=$.visibleCandles,z=W[W.length-1].close,Y=W[0].open,I=z-Y,V=Y===0?null:I/Y*100,K=I>=0?v.upCandle:v.downCandle,L=$.formatPrice(z);D.push(p$(L,{x:X,y:xX,fill:K,"font-size":t5,"font-weight":600,"font-family":R$})),X+=_1(L,t5)+oR;let N=$.instrument?.priceDecimals??5;return D.push(p$(P0(I,V,N),{x:X,y:xX,fill:K,"font-size":PO,"font-weight":500,"font-family":R$})),o(D.join(""))}function eO($,v){if(!v)return"";let D=bO;return pO({href:dO,x:$.plotRight-D-8,y:$.plotBottom-D-8,width:D,height:D,opacity:qO})}var eR=["price","priceHigh","priceLow","price1","price2","entry","stop","target"];function $H($,v){let D=$.candles.slice(-$.bars),X=kO,U=SO,G=Math.max(X+1,$.width-EO),J=Math.max(U+1,$.height-TO),Q=G-X,W=J-U,z=Q/Math.max(1,D.length),Y=Math.min(...D.map((a)=>a.low)),I=Math.max(...D.map((a)=>a.high)),V=I-Y,K=V>0?V*0.06:Math.max(Math.abs(I)*0.006,0.000001),L=Y-K,N=I+K,Z=Math.max(N-L,0.000001),F=V>0?Math.max(Z,V/0.6):Z,S=Dg($.shapes),g=S.length>0?Math.min(...S):Number.POSITIVE_INFINITY,u=S.length>0?Math.max(...S):Number.NEGATIVE_INFINITY,k=Number.isFinite(g)&&g<L?L-g:0,N$=Number.isFinite(u)&&u>N?u-N:0,j$=Math.max(0,F-Z),g4=k+N$,Q4=L,y$=N;if(g4<=j$)Q4-=k,y$+=N$;else if(k>0&&N$===0)Q4-=Math.min(k,j$);else if(N$>0&&k===0)y$+=Math.min(N$,j$);else if(g4>0){let a=j$/g4;Q4-=k*a,y$+=N$*a}let h1=Math.max(y$-Q4,0.000001),m1=Qg(D),c1=D.length-1;function q0(a){return U+(y$-a)/h1*W}function xH(a){if(!Number.isFinite(a))return{y:J,clamped:!0,edge:"bottom"};if(a>y$)return{y:U,clamped:!0,edge:"top"};if(a<Q4)return{y:J,clamped:!0,edge:"bottom"};return{y:q0(a),clamped:!1,edge:null}}function eX(a){return X+(a+0.5)*z}function H7(a){return Ug(D,c1,m1,a)}function F7(a){return eX(H7(a))}function j7(a){return c1-a}function fH(a,A7,yH){let w7=Gg(a,A7);if(w7!==void 0)return F7(w7);let nH=Jg(a,A7)??yH;return eX(j7(nH))}function _H(a=6){return sR(Q4,y$,a)}function iH(a=7){return vg(D.length,a)}return{pair:$.pair,timeframe:$.timeframe,theme:$.theme,width:$.width,height:$.height,plotLeft:X,plotTop:U,plotRight:G,plotBottom:J,plotWidth:Q,plotHeight:W,slot:z,visibleCandles:D,instrument:v,priceRange:{min:Q4,max:y$,candleMin:Y,candleMax:I},averageSpacing:m1,formatPrice(a){return mO(v,a)},priceToY:q0,projectPrice:xH,indexToX:eX,timeToIndex:H7,timeToX:F7,barsBackToIndex:j7,resolveEndpointX:fH,nicePriceTicks:_H,niceTimeTickIndices:iH}}var sO=3;function sR($,v,D=6){let X=Wg($,v,D),U=Math.ceil($/X)*X,G=Math.floor(v/X)*X,J=zg(X),Q=[];if(G>=U){let W=Math.round((G-U)/X);for(let z=0;z<=W;z+=1)Q.push(Number((U+z*X).toFixed(J+2)))}if(Q.length<sO)return $g($,v,sO+2);return Q}function $g($,v,D){if(v<=$||D<2)return[$];let X=[];for(let U=0;U<D;U+=1)X.push($+(v-$)*U/(D-1));return X}function vg($,v=7){if($<=0)return[];if($<=v)return Array.from({length:$},(U,G)=>G);let D=new Set,X=($-1)/Math.max(1,v-1);for(let U=0;U<v;U+=1)D.add(Math.round(U*X));return D.add(0),D.add($-1),Array.from(D).sort((U,G)=>U-G)}function Ug($,v,D,X){if($.length===1){let J=$[0].time;return(X-J)/D}let U=$[0],G=$[v];if(X<=U.time)return(X-U.time)/D;if(X>=G.time)return v+(X-G.time)/D;return Xg($,v,X)}function Dg($){let v=[];for(let D of $){if(D.affectsScale===!1)continue;for(let X of eR){let U=D[X];if(typeof U==="number"&&Number.isFinite(U))v.push(U)}}return v}function Xg($,v,D){let X=0,U=v;while(X<=U){let Y=Math.floor((X+U)/2),I=$[Y];if(I.time===D)return Y;if(I.time<D)X=Y+1;else U=Y-1}let G=Math.min(v,X),J=Math.max(0,G-1),Q=$[J],z=$[G].time-Q.time;if(z<=0)return J;return J+(D-Q.time)/z}function Gg($,v){if(v==="start")return $.time1??$.entryTime;return $.time2}function Jg($,v){if(v==="start")return $.barsBack1;return $.barsBack2}function Qg($){if($.length<2)return 1;let v=0,D=0;for(let X=1;X<$.length;X+=1){let U=$[X].time-$[X-1].time;if(U>0)v+=U,D+=1}if(D===0)return 1;return v/D}function Wg($,v,D){let U=Math.max(v-$,0.000001)/Math.max(1,D-1),G=Math.floor(Math.log10(U)),J=[G-1,G,G+1],Q=[1,2,2.5,5,10],W=U,z=Number.POSITIVE_INFINITY;for(let Y of J){let I=10**Y;for(let V of Q){let K=V*I,L=Math.ceil($/K),N=Math.floor(v/K),Z=N>=L?N-L+1:1,F=Math.abs(Z-D);if(F<z)z=F,W=K}}return W}function zg($){let v=0,D=$;while(v<10&&Math.abs(Math.round(D)-D)>0.00000001)D*=10,v+=1;return v}function Z6($){return CX[$]}function vH($,v){let D=Z6($.theme),X=Yg($.nicePriceTicks(),$.formatPrice),U=$.niceTimeTickIndices(),G=[],J=[],Q=$.visibleCandles[$.visibleCandles.length-1],W=$.priceToY(Q.close),z=Q.close>=Q.open?D.upCandle:D.downCandle,Y=$.formatPrice(Q.close);for(let N of X){let Z=$.priceToY(N);G.push(I$({x1:$.plotLeft,y1:Math.round(Z)+0.5,x2:$.plotRight,y2:Math.round(Z)+0.5,stroke:D.grid,"stroke-width":1}));let F=$.formatPrice(N);if(F!==Y)J.push(p$(F,{x:$.width-6,y:Z,fill:D.text,"font-size":d5,"font-family":R$,"text-anchor":"end","dominant-baseline":"middle"}))}for(let N of U){let Z=$.visibleCandles[N];if(!Z)continue;let F=$.indexToX(N),g=(N>0?$.visibleCandles[N-1]:void 0)?.time;G.push(I$({x1:Math.round(F)+0.5,y1:$.plotTop,x2:Math.round(F)+0.5,y2:$.plotBottom,stroke:D.grid,"stroke-width":1})),J.push(p$(cO($.timeframe,Z.time,g),{x:F,y:$.height-8,fill:D.text,"font-size":d5,"font-family":R$,"text-anchor":"middle"}))}J.push(I$({x1:Math.round($.plotRight)+0.5,y1:$.plotTop,x2:Math.round($.plotRight)+0.5,y2:$.plotBottom,stroke:D.axisBorder,"stroke-width":1})),J.push(I$({x1:$.plotLeft,y1:Math.round($.plotBottom)+0.5,x2:$.width,y2:Math.round($.plotBottom)+0.5,stroke:D.axisBorder,"stroke-width":1}));let I=I$({x1:$.plotLeft,y1:Math.round(W)+0.5,x2:$.plotRight,y2:Math.round(W)+0.5,stroke:z,"stroke-width":1,"stroke-dasharray":"4 4"}),V={y:W,label:$.formatPrice(Q.close),fill:z,height:f1},K=aO($,D),L=eO($,v);return{gridSvg:o(G.join(""),{"shape-rendering":"crispEdges"}),axesSvg:J.join(""),lastPriceSvg:I,lastPriceTag:V,headerSvg:K,watermarkSvg:L}}function Yg($,v){let D=new Set,X=[];for(let U of $){let G=v(U);if(!D.has(G))D.add(G),X.push(U)}return X}function UH($){let v=Math.max(1,Math.round($.slot*0.7)),D=[];for(let X=0;X<$.visibleCandles.length;X+=1){let U=$.visibleCandles[X];if(!U)continue;let G=U.close>=U.open?"#10b981":"#ef4444",J=$.indexToX(X),Q=Math.round(J)+0.5,W=$.priceToY(U.high),z=$.priceToY(U.low),Y=$.priceToY(U.open),I=$.priceToY(U.close),V=Math.min(Y,I),K=Math.max(Y,I),L=Math.max(1,Math.round(K-V)),N=Math.round(J-v/2),Z=Math.round(V);D.push(I$({x1:Q,y1:Math.round(W)+0.5,x2:Q,y2:Math.round(z)+0.5,stroke:G,"stroke-width":1})),D.push(F$({x:N,y:Z,width:v,height:L,fill:G}))}return o(D.join(""),{"shape-rendering":"crispEdges"})}function DH($,v){if(v.length===0)return"";let D=Z6($.theme);return Ig(v,$.plotTop+2,$.plotBottom-2).map((U)=>o([F$({x:$.plotRight+1,y:Math.round(U.y-U.height/2),width:$.width-$.plotRight-1,height:U.height,fill:U.fill}),p$(U.label,{x:$.width-6,y:U.y,fill:D.tagText,"font-size":U.height>=f1?11:10,"font-family":R$,"font-weight":600,"text-anchor":"end","dominant-baseline":"middle"})].join(""),{"shape-rendering":"crispEdges"})).join("")}function Ig($,v,D){let X=$.map((J)=>({...J})).sort((J,Q)=>J.y-Q.y);if(X.length===0)return X;for(let J=1;J<X.length;J+=1){let Q=X[J-1],W=X[J],z=Q.y+(Q.height+W.height)/2;if(W.y<z)W.y=z}let U=X[X.length-1];if(U.y>D){U.y=D;for(let J=X.length-2;J>=0;J-=1){let Q=X[J+1],W=X[J],z=Q.y-(Q.height+W.height)/2;if(W.y>z)W.y=z}}let G=X[0];if(G.y<v)G.y=v;return X}function s5($){let v=Lg($.candles);if(v.length===0)throw Error("Cannot render chart: candles is empty");return{pair:$.pair,timeframe:$.timeframe,candles:v,shapes:$.shapes??[],theme:$.theme??"dark",width:XH($.width,CO,_O,iO),height:XH($.height,uO,yO,nO),bars:Vg($.bars??o5),scale:Kg($.scale),watermark:$.watermark??fO,...$.fontFiles?{fontFiles:$.fontFiles}:{}}}function Vg($){if(!Number.isFinite($))return o5;return $7(Math.round($),RO,gO)}function Kg($){if($===void 0||!Number.isFinite($)||$<=0)return xO;return $7($,lO,hO)}function XH($,v,D,X){if($===void 0||!Number.isFinite($)||$<=0)return v;return $7(Math.round($),D,X)}function $7($,v,D){return Math.min(D,Math.max(v,$))}function Lg($){return $.filter((v)=>Number.isFinite(v.time)&&Number.isFinite(v.open)&&Number.isFinite(v.high)&&Number.isFinite(v.low)&&Number.isFinite(v.close)).sort((v,D)=>v.time-D.time)}import{createRequire as Bg}from"node:module";var Ng=Bg(import.meta.url);function GH($,v,D,X){let{Resvg:U}=Zg();return new U($,{fitTo:{mode:"width",value:Math.round(v*D)},font:{loadSystemFonts:!0,defaultFontFamily:"Inter",...X?{fontFiles:X}:{}},background:"rgba(0,0,0,0)"}).render().asPng()}function Zg(){try{return Ng("@resvg/resvg-js")}catch($){let v=$ instanceof Error?$.message:String($);throw Error(`chart-render: @resvg/resvg-js could not be loaded for PNG rasterization (${v})`)}}var Og={horizontal_line:"horizontal_line",line:"horizontal_line",hline:"horizontal_line",horizontal:"horizontal_line",horz_line:"horizontal_line",horizontal_ray:"horizontal_ray",hray:"horizontal_ray",horz_ray:"horizontal_ray",horizontalray:"horizontal_ray",h_ray:"horizontal_ray",vertical_line:"vertical_line",vertical:"vertical_line",vline:"vertical_line",vert_line:"vertical_line",vertline:"vertical_line",verticalline:"vertical_line",cross_line:"cross_line",crossline:"cross_line",cross:"cross_line",zone:"zone",horizontal_range:"zone",range:"zone",supply_demand_zone:"zone",rectangle:"rectangle",rect:"rectangle",box:"rectangle",trendline:"trendline",trend:"trendline",trend_line:"trendline",ray:"ray",trend_ray:"ray",right_ray:"right_ray",ray_right:"right_ray",rightray:"right_ray","right-ray":"right_ray",left_ray:"left_ray",ray_left:"left_ray",leftray:"left_ray","left-ray":"left_ray",extended_line:"extended_line",extendedline:"extended_line",extended:"extended_line",extend_line:"extended_line",info_line:"info_line",infoline:"info_line",info:"info_line",trend_angle:"trend_angle",trendangle:"trend_angle",angle:"trend_angle",position:"position",trade:"position",long:"position",short:"position",long_position:"position",short_position:"position"},Hg={horizontal_line:{required:[{field:"price",message:"price is required for horizontal_line"}],describe:($)=>`Drew horizontal line at ${$.price}${$.label?` (${$.label})`:""}`},horizontal_ray:{required:[{field:"price",message:"price is required for horizontal_ray"}],describe:($)=>`Drew horizontal ray at ${$.price}${$.label?` (${$.label})`:""}`},vertical_line:{required:[],describe:($)=>{let v=$.time1??$.time2;return`Drew vertical line${v?` at ${v}`:""}${$.label?` (${$.label})`:""}`}},cross_line:{required:[{field:"price",message:"price is required for cross_line"}],describe:($)=>`Drew cross line at ${$.price}${$.label?` (${$.label})`:""}`},zone:{required:[{field:"priceHigh",message:"priceHigh is required for zone"},{field:"priceLow",message:"priceLow is required for zone"}],describe:($)=>`Drew zone from ${$.priceLow} to ${$.priceHigh}${$.label?` (${$.label})`:""}`},rectangle:{required:[{field:"priceHigh",message:"priceHigh is required for rectangle"},{field:"priceLow",message:"priceLow is required for rectangle"}],describe:($)=>`Drew rectangle from ${$.priceLow} to ${$.priceHigh}${$.label?` (${$.label})`:""}`},trendline:{required:[{field:"price1",message:"price1 is required for trendline"},{field:"price2",message:"price2 is required for trendline"}],describe:($)=>`Drew trendline from ${$.price1} to ${$.price2}${$.label?` (${$.label})`:""}`},ray:{required:[{field:"price1",message:"price1 is required for ray"},{field:"price2",message:"price2 is required for ray"}],describe:($)=>`Drew ray from ${$.price1} to ${$.price2}${$.label?` (${$.label})`:""}`},right_ray:{required:[{field:"price1",message:"price1 is required for right_ray"},{field:"price2",message:"price2 is required for right_ray"}],describe:($)=>`Drew right ray from ${$.price1} to ${$.price2}${$.label?` (${$.label})`:""}`},left_ray:{required:[{field:"price1",message:"price1 is required for left_ray"},{field:"price2",message:"price2 is required for left_ray"}],describe:($)=>`Drew left ray from ${$.price1} to ${$.price2}${$.label?` (${$.label})`:""}`},extended_line:{required:[{field:"price1",message:"price1 is required for extended_line"},{field:"price2",message:"price2 is required for extended_line"}],describe:($)=>`Drew extended line from ${$.price1} to ${$.price2}${$.label?` (${$.label})`:""}`},info_line:{required:[{field:"price1",message:"price1 is required for info_line"},{field:"price2",message:"price2 is required for info_line"}],describe:($)=>`Drew info line from ${$.price1} to ${$.price2}${$.label?` (${$.label})`:""}`},trend_angle:{required:[{field:"price1",message:"price1 is required for trend_angle"},{field:"price2",message:"price2 is required for trend_angle"}],describe:($)=>`Drew trend angle from ${$.price1} to ${$.price2}${$.label?` (${$.label})`:""}`},position:{required:[{field:"direction",message:'direction ("long" or "short") is required for position'},{field:"entry",message:"entry is required for position"},{field:"stop",message:"stop is required for position"},{field:"target",message:"target is required for position"}],describe:($)=>`Drew ${$.direction} position — entry ${$.entry}, stop ${$.stop}, target ${$.target}${$.label?` (${$.label})`:""}`}};var Fg=["horizontal_line","horizontal_ray","vertical_line","cross_line","zone","rectangle","trendline","trend_line","ray","right_ray","left_ray","extended_line","info_line","trend_angle","position"];function b0($){if(typeof $!=="string")return null;let v=$.trim().toLowerCase();return Og[v]??null}function WH($){return`Unknown shape type: "${String($)}". Supported types: ${Fg.join(", ")}`}function v7($){if(typeof $==="number"&&Number.isFinite($))return $;if(typeof $==="string"){let v=$.trim();if(v.length===0)return;let D=Number(v);if(Number.isFinite(D))return D}return}function jg($){if(typeof $!=="string")return;let v=$.trim().toLowerCase();if(v==="long"||v==="buy")return"long";if(v==="short"||v==="sell")return"short";return}function t$($,v){for(let D of v){let X=v7($[D]);if(X!==void 0)return X}return}function R4($,v,D){let X=$[v];if(!X||typeof X!=="object"||Array.isArray(X))return;return t$(X,D)}function Ag($){if(typeof $.type!=="string")return null;let v=$.type.trim().toLowerCase();return v.length>0?v:null}function O4($,v,D){if($[v]!==void 0||D===void 0)return;$[v]=D}function U7($,v,D){O4($,v,D===void 0?void 0:Math.trunc(D))}function wg($,v){if($.direction)return;if(v==="long"||v==="short"){$.direction=v;return}if(v==="long_position"||v==="short_position"){$.direction=v==="long_position"?"long":"short";return}$.direction=jg($.side)}function Mg($){return t$($,["level","y","value","priceLevel","linePrice"])??R4($,"point1",["price","y","value"])??R4($,"from",["price","y","value"])}function JH($,v){if(v==="start")return t$($,["startPrice","fromPrice","priceA","p1"])??R4($,"point1",["price","y","value"])??R4($,"from",["price","y","value"]);return t$($,["endPrice","toPrice","priceB","p2"])??R4($,"point2",["price","y","value"])??R4($,"to",["price","y","value"])}function QH($,v){if(v==="start")return t$($,["time","startTime","fromTime","timestamp","t1"])??R4($,"point1",["time","t","timestamp"])??R4($,"from",["time","t","timestamp"]);return t$($,["endTime","toTime","t2"])??R4($,"point2",["time","t","timestamp"])??R4($,"to",["time","t","timestamp"])}function Pg($){O4($,"price",Mg($)),O4($,"priceHigh",t$($,["high","upper","top","max","priceTop","priceUpper"])),O4($,"priceLow",t$($,["low","lower","bottom","min","priceBottom","priceLower"])),O4($,"price1",JH($,"start")),O4($,"price2",JH($,"end"))}function bg($){U7($,"time1",QH($,"start")),U7($,"time2",QH($,"end")),O4($,"barsBack1",t$($,["barsBack","barBack","lookback","bars"])),O4($,"barsBack2",t$($,["barsBackEnd","barBackEnd","lookbackEnd"]))}function qg($){O4($,"entry",t$($,["entryPrice","open","openPrice"])),O4($,"stop",t$($,["stopLoss","sl","stopPrice"])),O4($,"target",t$($,["takeProfit","tp","targetPrice","tpPrice"])),U7($,"entryTime",t$($,["entryTimestamp"]))}function Sg($){let v=v7($.priceHigh),D=v7($.priceLow);if(v!==void 0&&D!==void 0&&v<D)$.priceHigh=D,$.priceLow=v}function zH($){if(typeof $!=="object"||$===null||Array.isArray($))return $;let v={...$},D=Ag(v),X=b0(D);if(X)v.type=X;return wg(v,D),Pg(v),bg(v),qg(v),Sg(v),v}function YH($){return Hg[$].required}var kg=l.object({type:l.string().describe('Shape type: "horizontal_line", "horizontal_ray", "vertical_line", "cross_line", "zone", "rectangle", "trendline", "ray", "right_ray", "left_ray", "extended_line", "info_line", "trend_angle", or "position"'),price:l.coerce.number().optional().describe("Price level (required for horizontal_line, horizontal_ray, cross_line)"),priceHigh:l.coerce.number().optional().describe("Upper price boundary (required for zone/rectangle)"),priceLow:l.coerce.number().optional().describe("Lower price boundary (required for zone/rectangle)"),price1:l.coerce.number().optional().describe("First point price (required for trendline/ray/right_ray/left_ray/extended_line/info_line/trend_angle)"),price2:l.coerce.number().optional().describe("Second point price (required for trendline/ray/right_ray/left_ray/extended_line/info_line/trend_angle)"),time1:l.number().int().optional().describe("Absolute time (epoch seconds) of the first endpoint for trendline/rays or anchor for vertical_line. PREFERRED over barsBack1."),time2:l.number().int().optional().describe("Absolute time (epoch seconds) of the second endpoint (trendline/rays) or right edge of a zone. PREFERRED over barsBack2."),barsBack1:l.number().optional().describe("FALLBACK only — bars back from the current bar for first endpoint/anchor (trendline, rays, vertical_line). Use time1 when possible."),barsBack2:l.number().optional().describe("FALLBACK only — bars back from the current bar for second endpoint / zone right edge. Use time2 when possible."),direction:l.enum(["long","short"]).optional().describe('Position direction (required for position): "long" or "short"'),entry:l.coerce.number().optional().describe("Entry price (required for position)"),stop:l.coerce.number().optional().describe("Stop-loss price (required for position)"),target:l.coerce.number().optional().describe("Take-profit price (required for position)"),entryTime:l.number().int().optional().describe("Absolute time (epoch seconds) for the position entry anchor. Defaults to the current bar."),label:l.string().optional().describe('Label text, e.g. "Support", "Resistance", "Demand Zone", "Long EURCHF"'),color:l.string().optional().describe('Color hex, e.g. "#22c55e" green, "#ef4444" red, "#3b82f6" blue'),affectsScale:l.boolean().optional().describe("Whether this shape expands the rendered price range (default true). Set false for distant context levels that should stay labelled but not compress the setup.")}).superRefine(($,v)=>{let D=b0($.type);if(!D){v.addIssue({code:"custom",message:WH($.type),path:["type"]});return}for(let X of YH(D))if($[X.field]===void 0)v.addIssue({code:"custom",message:X.message,path:[X.field]})}),IH=l.preprocess(zH,kg),Jo=l.object({shapes:l.array(IH).min(1)});function VH($){let v=[],D=[];for(let[X,U]of $.entries()){let G=IH.safeParse(U);if(G.success)v.push(G.data);else D.push({index:X,reason:G.error.issues[0]?.message??"invalid shape"})}return{valid:v,invalid:D}}function $4($,v,D){let X={type:$.type,ok:!0,...D?{reason:D}:{},...$.label?{label:$.label}:{}};return{underlaySvg:v.underlaySvg??"",lineSvg:v.lineSvg??"",positionSvg:v.positionSvg??"",chromeSvg:v.chromeSvg??"",gutterTags:v.gutterTags??[],report:X}}function O$($,v){let D={type:$.type,ok:!1,reason:v,...$.label?{label:$.label}:{}};return{underlaySvg:"",lineSvg:"",positionSvg:"",chromeSvg:"",gutterTags:[],report:D}}function k$($){return typeof $==="number"&&Number.isFinite($)?$:void 0}function o$($,v){return $.color&&$.color.trim().length>0?$.color:v}function z$($,v,D,X){let U=$.projectPrice(D);if(!U.clamped||U.edge===null)return"";let G=Z6($.theme),J=Y$(v,$.plotLeft+10,$.plotRight-10),Q=U.edge==="top"?$.plotTop+4:$.plotBottom-4,W=U.edge==="top"?[{x:J,y:Q},{x:J-5,y:Q+7},{x:J+5,y:Q+7}]:[{x:J,y:Q},{x:J-5,y:Q-7},{x:J+5,y:Q-7}],z=U.edge==="top"?Q+16:Q-10;return o([tO(W,{fill:X}),p$($.formatPrice(D),{x:J,y:z,fill:G.markerText,"font-size":10,"font-family":R$,"text-anchor":"middle"})].join(""))}function D7($,v,D,X,U){let G=_1(D,10)+10;return o([F$({x:$,y:v-10,width:G,height:16,fill:X,"fill-opacity":0.85}),p$(D,{x:$+5,y:v+1,fill:U,"font-size":10,"font-family":R$})].join(""),{"shape-rendering":"crispEdges"})}function H4($,v,D,X,U="start"){return p$(D,{x:$,y:v,fill:X,"font-size":10,"font-family":R$,"text-anchor":U})}function Y$($,v,D){return Math.min(D,Math.max(v,$))}var LH="price out of visible range (clamped)";function X7($,v){if(v.type==="zone")return Eg($,v);if(v.type==="rectangle")return Tg($,v);return O$(v,`Unsupported area shape: ${v.type}`)}function Eg($,v){let D=BH($,v,"zone");if(D.kind==="error")return D.fragments;let X=o$(v,"#3b82f6"),U=Y$(D.top+12,$.plotTop+12,$.plotBottom-6),G=(D.left+D.right)/2;return $4(v,{underlaySvg:o(F$({x:D.left,y:D.top,width:D.right-D.left,height:D.bottom-D.top,fill:X,"fill-opacity":0.12}),{"shape-rendering":"crispEdges"}),lineSvg:o([I$({x1:D.left,y1:Math.round(D.top)+0.5,x2:D.right,y2:Math.round(D.top)+0.5,stroke:X,"stroke-width":1}),I$({x1:D.left,y1:Math.round(D.bottom)+0.5,x2:D.right,y2:Math.round(D.bottom)+0.5,stroke:X,"stroke-width":1}),v.label?H4(D.left+4,U,v.label,X):"",z$($,G,D.highPrice,X),z$($,G,D.lowPrice,X)].join(""),{"shape-rendering":"crispEdges"})},D.clamped?LH:void 0)}function Tg($,v){let D=BH($,v,"rectangle");if(D.kind==="error")return D.fragments;let X=o$(v,"#f59e0b"),U=Y$(D.top+12,$.plotTop+12,$.plotBottom-6),G=(D.left+D.right)/2;return $4(v,{underlaySvg:o(F$({x:D.left,y:D.top,width:D.right-D.left,height:D.bottom-D.top,fill:X,"fill-opacity":0.12}),{"shape-rendering":"crispEdges"}),lineSvg:o([F$({x:D.left,y:D.top,width:D.right-D.left,height:D.bottom-D.top,fill:"none",stroke:X,"stroke-width":1}),v.label?H4(D.left+4,U,v.label,X):"",z$($,G,D.highPrice,X),z$($,G,D.lowPrice,X)].join(""),{"shape-rendering":"crispEdges"})},D.clamped?LH:void 0)}function BH($,v,D){let X=k$(v.priceHigh),U=k$(v.priceLow);if(X===void 0)return{kind:"error",fragments:O$(v,`priceHigh is required for ${D}`)};if(U===void 0)return{kind:"error",fragments:O$(v,`priceLow is required for ${D}`)};let G=$.projectPrice(X),J=$.projectPrice(U),Q=D==="zone"&&!KH(v)?$.plotLeft:Y$($.resolveEndpointX(v,"start",24),$.plotLeft,$.plotRight),W=D==="zone"&&!KH(v)?$.plotRight:Y$($.resolveEndpointX(v,"end",4),$.plotLeft,$.plotRight),z=Math.min(G.y,J.y),Y=Math.max(G.y,J.y);return{kind:"ok",left:Math.min(Q,W),right:Math.max(Q,W),top:z,bottom:Y,highPrice:X,lowPrice:U,clamped:G.clamped||J.clamped}}function KH($){return $.time1!==void 0||$.time2!==void 0||$.barsBack1!==void 0||$.barsBack2!==void 0}function fX($,v,D){if(Math.abs(D.x-v.x)<0.000001)return v.y;let X=(D.y-v.y)/(D.x-v.x);return v.y+($-v.x)*X}function NH($,v,D){if(Math.abs(v.x-$.x)<0.000001)return{start:{x:$.x,y:$.y},end:{x:$.x,y:v.y}};return{start:{x:$.x,y:$.y},end:{x:D,y:fX(D,$,v)}}}function ZH($,v,D){if(Math.abs(v.x-$.x)<0.000001)return{start:{x:$.x,y:$.y},end:{x:$.x,y:v.y}};return{start:{x:$.x,y:$.y},end:{x:D,y:fX(D,$,v)}}}function OH($,v,D,X,U,G){if(Math.abs(v.x-$.x)<0.000001)return{start:{x:$.x,y:U},end:{x:$.x,y:G}};return{start:{x:D,y:fX(D,$,v)},end:{x:X,y:fX(X,$,v)}}}function G7($,v){return{x:($.x+v.x)/2,y:($.y+v.y)/2}}var y1="price out of visible range (clamped)";function J4($,v){switch(v.type){case"horizontal_line":return Rg($,v);case"horizontal_ray":return gg($,v);case"vertical_line":return Cg($,v);case"cross_line":return ug($,v);case"trendline":return xg($,v);case"ray":case"right_ray":return fg($,v);case"left_ray":return _g($,v);case"extended_line":return ig($,v);case"info_line":return yg($,v);case"trend_angle":return ng($,v);default:return O$(v,`Unsupported line shape: ${v.type}`)}}function Rg($,v){let D=k$(v.price);if(D===void 0)return O$(v,"price is required for horizontal_line");let X=o$(v,"#3b82f6"),U=$.projectPrice(D),G=[I$({x1:$.plotLeft,y1:Math.round(U.y)+0.5,x2:$.plotRight,y2:Math.round(U.y)+0.5,stroke:X,"stroke-width":1}),v.label?H4($.plotLeft+4,Y$(U.y-4,$.plotTop+10,$.plotBottom-4),v.label,X):"",z$($,$.plotRight-12,D,X)].join("");return $4(v,{lineSvg:o(G,{"shape-rendering":"crispEdges"}),gutterTags:[{y:U.y,label:$.formatPrice(D),fill:X,height:f1}]},U.clamped?y1:void 0)}function gg($,v){let D=k$(v.price);if(D===void 0)return O$(v,"price is required for horizontal_ray");let X=o$(v,"#3b82f6"),U=$.projectPrice(D),G=$.resolveEndpointX(v,"start",0),J=Y$(G+4,$.plotLeft+4,$.plotRight-60);return $4(v,{lineSvg:o([I$({x1:Y$(G,$.plotLeft,$.plotRight),y1:Math.round(U.y)+0.5,x2:$.plotRight,y2:Math.round(U.y)+0.5,stroke:X,"stroke-width":1}),v.label?H4(J,Y$(U.y-4,$.plotTop+10,$.plotBottom-4),v.label,X):"",z$($,G,D,X)].join(""),{"shape-rendering":"crispEdges"})},U.clamped?y1:void 0)}function Cg($,v){let D=o$(v,"#f59e0b"),X=$.resolveEndpointX(v,"start",0),U=Y$(X,$.plotLeft,$.plotRight);return $4(v,{lineSvg:o([I$({x1:Math.round(U)+0.5,y1:$.plotTop,x2:Math.round(U)+0.5,y2:$.plotBottom,stroke:D,"stroke-width":1,"stroke-dasharray":"4 4"}),v.label?H4(U+4,$.plotTop+12,v.label,D):""].join(""),{"shape-rendering":"crispEdges"})})}function ug($,v){let D=k$(v.price);if(D===void 0)return O$(v,"price is required for cross_line");let X=o$(v,"#38bdf8"),U=$.projectPrice(D),G=Y$($.resolveEndpointX(v,"start",0),$.plotLeft,$.plotRight);return $4(v,{lineSvg:o([I$({x1:$.plotLeft,y1:Math.round(U.y)+0.5,x2:$.plotRight,y2:Math.round(U.y)+0.5,stroke:X,"stroke-width":1,"stroke-dasharray":"4 4"}),I$({x1:Math.round(G)+0.5,y1:$.plotTop,x2:Math.round(G)+0.5,y2:$.plotBottom,stroke:X,"stroke-width":1,"stroke-dasharray":"4 4"}),v.label?H4(G+4,Y$(U.y-4,$.plotTop+10,$.plotBottom-4),v.label,X):"",z$($,G,D,X)].join(""),{"shape-rendering":"crispEdges"})},U.clamped?y1:void 0)}function xg($,v){let D=n1($,v,"trendline");if(D.kind==="error")return D.fragments;return lg($,v,D.first,D.second,"#f59e0b","4 4")}function fg($,v){let D=n1($,v,v.type);if(D.kind==="error")return D.fragments;let X=o$(v,"#f59e0b"),U=NH(D.first,D.second,$.plotRight);return _X($,v,U.start,U.end,X,"4 4",[z$($,D.first.x,D.price1,X),z$($,D.second.x,D.price2,X)])}function _g($,v){let D=n1($,v,"left_ray");if(D.kind==="error")return D.fragments;let X=o$(v,"#f59e0b"),U=ZH(D.first,D.second,$.plotLeft);return _X($,v,U.start,U.end,X,"4 4",[z$($,D.first.x,D.price1,X),z$($,D.second.x,D.price2,X)])}function ig($,v){let D=n1($,v,"extended_line");if(D.kind==="error")return D.fragments;let X=o$(v,"#f59e0b"),U=OH(D.first,D.second,$.plotLeft,$.plotRight,$.plotTop,$.plotBottom);return _X($,v,U.start,U.end,X,"4 4",[z$($,D.first.x,D.price1,X),z$($,D.second.x,D.price2,X)])}function yg($,v){let D=n1($,v,"info_line");if(D.kind==="error")return D.fragments;let X=o$(v,"#22c55e"),U=D.price2-D.price1,G=D.price1===0?null:U/D.price1*100,J=$.instrument?.priceDecimals??5,Q=v.label?`${v.label} · ${P0(U,G,J)}`:P0(U,G,J),W=G7(D.first,D.second);return $4(v,{lineSvg:o([I$({x1:D.first.x,y1:D.first.y,x2:D.second.x,y2:D.second.y,stroke:X,"stroke-width":1}),D7(Y$(W.x-30,$.plotLeft+2,$.plotRight-120),Y$(W.y-6,$.plotTop+12,$.plotBottom-12),Q,X,"#FFFFFF"),z$($,D.first.x,D.price1,X),z$($,D.second.x,D.price2,X)].join(""),{"shape-rendering":"crispEdges"})},D.clamped?y1:void 0)}function ng($,v){let D=n1($,v,"trend_angle");if(D.kind==="error")return D.fragments;let X=o$(v,"#a855f7"),U=Math.atan2(D.first.y-D.second.y,D.second.x-D.first.x)*180/Math.PI,G=v.label?`${v.label} · ${U.toFixed(1)}°`:`${U.toFixed(1)}°`,J=G7(D.first,D.second);return $4(v,{lineSvg:o([I$({x1:D.first.x,y1:D.first.y,x2:D.second.x,y2:D.second.y,stroke:X,"stroke-width":1}),D7(Y$(J.x-26,$.plotLeft+2,$.plotRight-100),Y$(J.y-6,$.plotTop+12,$.plotBottom-12),G,X,"#FFFFFF"),z$($,D.first.x,D.price1,X),z$($,D.second.x,D.price2,X)].join(""),{"shape-rendering":"crispEdges"})},D.clamped?y1:void 0)}function lg($,v,D,X,U,G){let J=o$(v,U);return _X($,v,D,X,J,G,[z$($,D.x,k$(v.price1)??0,J),z$($,X.x,k$(v.price2)??0,J)])}function _X($,v,D,X,U,G,J){let Q=Y$(X.x+4,$.plotLeft+4,$.plotRight-70),W=Y$(X.y-4,$.plotTop+12,$.plotBottom-4),z=k$(v.price1),Y=k$(v.price2),I=z!==void 0&&$.projectPrice(z).clamped||Y!==void 0&&$.projectPrice(Y).clamped;return $4(v,{lineSvg:o([I$({x1:D.x,y1:D.y,x2:X.x,y2:X.y,stroke:U,"stroke-width":1,...G?{"stroke-dasharray":G}:{}}),v.label?H4(Q,W,v.label,U):"",...J].join(""),{"shape-rendering":"crispEdges"})},I?y1:void 0)}function n1($,v,D){let X=k$(v.price1),U=k$(v.price2);if(X===void 0)return{kind:"error",fragments:O$(v,`price1 is required for ${D}`)};if(U===void 0)return{kind:"error",fragments:O$(v,`price2 is required for ${D}`)};let G=$.projectPrice(X),J=$.projectPrice(U),Q={x:Y$($.resolveEndpointX(v,"start",50),$.plotLeft,$.plotRight),y:G.y},W={x:Y$($.resolveEndpointX(v,"end",0),$.plotLeft,$.plotRight),y:J.y};return{kind:"ok",first:Q,second:W,price1:X,price2:U,clamped:G.clamped||J.clamped}}var hg="price out of visible range (clamped)";function HH($,v){if(v.direction!=="long"&&v.direction!=="short")return O$(v,'direction ("long" or "short") is required for position');let D=k$(v.entry),X=k$(v.stop),U=k$(v.target);if(D===void 0)return O$(v,"entry is required for position");if(X===void 0)return O$(v,"stop is required for position");if(U===void 0)return O$(v,"target is required for position");let G=Z6($.theme),J=Y$($.resolveEndpointX(v,"start",0),$.plotLeft,$.plotRight),Q=$.projectPrice(D),W=$.projectPrice(X),z=$.projectPrice(U),Y=Math.min(Q.y,z.y),I=Math.max(Q.y,z.y),V=Math.min(Q.y,W.y),K=Math.max(Q.y,W.y),L=Y$(Math.min(Y,V)+12,$.plotTop+12,$.plotBottom-8),N=Q.clamped||W.clamped||z.clamped;return $4(v,{positionSvg:o([F$({x:J,y:Y,width:$.plotRight-J,height:I-Y,fill:"#10b981","fill-opacity":0.12}),F$({x:J,y:V,width:$.plotRight-J,height:K-V,fill:"#ef4444","fill-opacity":0.12}),I$({x1:J,y1:Math.round(Q.y)+0.5,x2:$.plotRight,y2:Math.round(Q.y)+0.5,stroke:G.entryTag,"stroke-width":1}),I$({x1:J,y1:Math.round(W.y)+0.5,x2:$.plotRight,y2:Math.round(W.y)+0.5,stroke:"#ef4444","stroke-width":1}),I$({x1:J,y1:Math.round(z.y)+0.5,x2:$.plotRight,y2:Math.round(z.y)+0.5,stroke:"#10b981","stroke-width":1}),v.label?H4(J+4,L,v.label,G.text):"",z$($,J,D,G.entryTag),z$($,J,X,"#ef4444"),z$($,J,U,"#10b981")].join(""),{"shape-rendering":"crispEdges"}),gutterTags:[{y:Q.y,label:`Entry ${$.formatPrice(D)}`,fill:G.entryTag,height:gX},{y:W.y,label:`SL ${$.formatPrice(X)}`,fill:"#ef4444",height:gX},{y:z.y,label:`TP ${$.formatPrice(U)}`,fill:"#10b981",height:gX}]},N?hg:void 0)}var mg={horizontal_line:J4,horizontal_ray:J4,vertical_line:J4,cross_line:J4,zone:X7,rectangle:X7,trendline:J4,ray:J4,right_ray:J4,left_ray:J4,extended_line:J4,info_line:J4,trend_angle:J4,position:HH};function FH($,v){let D=[],X=[],U=[],G=[],J=[],Q=[],W=0,z=0;for(let Y of v){let I=cg($,Y);if(D.push(I.underlaySvg),X.push(I.lineSvg),U.push(I.positionSvg),G.push(I.chromeSvg),J.push(...I.gutterTags),Q.push(I.report),I.report.ok)W+=1;else z+=1}return{underlaySvg:D.join(""),lineSvg:X.join(""),positionSvg:U.join(""),chromeSvg:G.join(""),gutterTags:J,reports:Q,drawn:W,skipped:z}}function cg($,v){let D=b0(v.type);if(!D)return O$(v,`Unknown shape type: ${String(v.type)}`);let X=mg[D];if(!X)return O$(v,`Unsupported shape type: ${D}`);let U=D===v.type?v:{...v,type:D};return X($,U)}var J7;function jH($){let v=s5($),D=dg(v.pair),X=$H(v,D),U=CX[v.theme],G=FH(X,v.shapes),J=vH(X,v.watermark),Q=`plot-clip-${tg(X)}`,W=`<defs><clipPath id="${Q}">${F$({x:X.plotLeft,y:X.plotTop,width:X.plotWidth,height:X.plotHeight})}</clipPath></defs>`,z={"clip-path":`url(#${Q})`},Y=o(J.gridSvg,z),I=o(G.underlaySvg,z),V=o(UH(X),z),K=o(G.lineSvg,z),L=o(G.positionSvg,z),N=DH(X,[...G.gutterTags,J.lastPriceTag]);return{svg:oO(X.width,X.height,[F$({x:0,y:0,width:X.width,height:X.height,fill:U.background}),W,Y,I,V,K,L,J.axesSvg,G.chromeSvg,J.lastPriceSvg,N,J.headerSvg,J.watermarkSvg,pg()].join("")),width:X.width,height:X.height,drawn:G.drawn,skipped:G.skipped,shapes:G.reports,priceRange:{min:X.priceRange.min,max:X.priceRange.max}}}function Q7($){let v=jH($),D=s5($);return{...v,png:GH(v.svg,v.width,D.scale,D.fontFiles)}}function dg($){return rg().resolve($)}function rg(){if(J7===void 0)J7=new M0([...TX]);return J7}function pg(){return`<style>text{font-family:${R$};white-space:pre}</style>`}function tg($){let v=$.visibleCandles,D=v[0]?.time??0,X=v[v.length-1]?.time??0,U=`${$.pair}|${$.timeframe}|${$.width}x${$.height}|${v.length}|${D}|${X}`,G=2166136261;for(let J=0;J<U.length;J+=1)G^=U.charCodeAt(J),G=Math.imul(G,16777619);return(G>>>0).toString(36)}var AH=120,W7=20,z7=200,og=4800;function ag($,v,D){return Math.max(v,Math.min(D,$))}var eg="Render an annotated candlestick chart image (PNG) for a pair + timeframe and optional market-data sourceId. Fetches recent candles and draws optional shapes (support/resistance lines, zones, trendlines, a long/short position) using the same vocabulary as draw_chart_shape. Returns the chart as an image plus a text summary. PREFER absolute time anchors (time1/time2/entryTime, epoch seconds from get_candles) over barsBack*.",sg="Optional annotations. Each item is {type, ...}: horizontal_line/horizontal_ray/cross_line need `price`; zone/rectangle need `priceHigh`+`priceLow`; trendline/ray/right_ray/left_ray/extended_line/info_line/trend_angle need `price1`+`price2` (prefer `time1`+`time2` epoch seconds); position needs `direction` (long/short)+`entry`+`stop`+`target`. Optional `label`, `color` (hex), and `affectsScale` (false keeps a distant contextual level labelled without compressing the active setup).",iX=[E({name:"render_chart",description:eg,inputSchema:{type:"object",properties:{pair:{type:"string",description:"Canonical instrument id, e.g. EURUSD (case-insensitive)"},timeframe:{type:"string",description:"e.g. 1m, 5m, 15m, 30m, 1h, 4h, 1d (or an integer multiple)"},sourceId:{type:"string",pattern:"^[a-z][a-z0-9-]{0,62}$",description:"Market-data source from get_market_config, e.g. mt5-primary. Omit for Twelve Data."},shapes:{type:"array",description:sg,items:{type:"object"}},bars:{type:"integer",minimum:W7,maximum:z7,description:`Most-recent candles to show (${W7}-${z7}, default ${AH})`},offset:{type:"integer",minimum:0,maximum:og,description:"Candle page offset: 0 is the latest page; larger offsets walk backward through history. Pair with bars to frame a historical setup."},theme:{type:"string",enum:["dark","light"],description:"Color theme (default dark)"},width:{type:"integer",minimum:320,maximum:2560,description:"Image width in px (320-2560, default 1280)"},height:{type:"integer",minimum:240,maximum:1440,description:"Image height in px (240-1440, default 720)"}},required:["pair","timeframe"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=ag($.bars??AH,W7,z7),{candles:X}=await qX(v.trpc,{pair:$.pair,timeframe:$.timeframe,limit:D,...$.offset===void 0?{}:{offset:$.offset},...$.sourceId?{sourceId:$.sourceId}:{}});if(X.length===0)throw Error(`No candles available for ${$.pair} ${$.timeframe}. Check the pair/timeframe with get_market_config.`);let{valid:U,invalid:G}=VH($.shapes??[]),J=Q7({pair:$.pair,timeframe:$.timeframe,candles:X,shapes:U,bars:D,...$.theme?{theme:$.theme}:{},...$.width?{width:$.width}:{},...$.height?{height:$.height}:{}}),Q=J.shapes.filter((I)=>!I.ok),W=[...G.map((I)=>`shape ${I.index}: ${I.reason}`),...Q.map((I)=>`${I.type}: ${I.reason??"undrawable"}`)],z=W.length>0?` (${W.length} skipped)
|
|
107
|
-
Skipped: ${W.join("; ")}`:"",Y=`Rendered ${$.pair} ${$.timeframe} — ${X.length} candles, drew ${J.drawn} shape${J.drawn===1?"":"s"}. Price range ${J.priceRange.min}–${J.priceRange.max}.${z}`;return{content:[{type:"image",data:Buffer.from(J.png).toString("base64"),mimeType:"image/png"},{type:"text",text:Y}]}}})];async function yX($){try{return{ok:!0,value:await $}}catch(v){return{ok:!1,error:v instanceof Error?v.message:String(v)}}}function $C($){return/unauthor|forbidden|401|403|sign[ -]?in|token|credential/i.test($)}var vC="The Aether Wealth backend is unreachable right now. Try again shortly.",UC=_5.flatMap(($)=>$.dashboardOnly.map((v)=>`${$.name}: ${v}`)),nX=[E({name:"get_context",description:"Snapshot the user's live Aether Wealth state in one call — accounts, open trades, recent performance stats, and active alerts — plus the map of dashboard-only actions. Call this first to ground suggestions in what the user is actually doing. Requires sign-in.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await Promise.all([yX(v.trpc.trading.accounts.list.query()),yX(v.trpc.trading.trades.list.query({status:"OPEN",limit:200})),yX(v.trpc.trading.analytics.stats.query({})),yX(v.trpc.alerts.list.query({}))]);if(D.every((W)=>!W.ok)){let W=D.every((z)=>!z.ok&&$C(z.error));throw Error(W?l5:vC)}let[X,U,G,J]=D,Q=(W)=>W.ok?W.value:{error:W.error};return{accounts:Q(X),openTrades:Q(U),stats:Q(G),alerts:Q(J),dashboard:{url:f4,dashboardOnly:UC,note:`These actions are not available over MCP. When the user needs one, suggest they open the dashboard at ${f4}.`}}}})];function DC(){return{url:process.env.AETHER_APP_PUSH_URL?.trim()||"http://127.0.0.1:9003",secret:process.env.AI_LIVE_PUSH_SECRET?.trim()||void 0}}async function u6($,v,D){let{url:X,secret:U}=DC(),G=`${X.replace(/\/$/,"")}/api/ai/live/push`,J;try{J=await fetch(G,{method:"POST",headers:{"content-type":"application/json",...U?{"x-live-push-secret":U}:{}},body:JSON.stringify({userId:$,tool:v,args:D})})}catch(z){throw Error(`Could not reach the Aether Wealth app at ${G}. Is the dashboard running?`,{cause:z})}if(!J.ok)throw Error(`Live-chart push rejected (HTTP ${J.status}).`);let Q=await J.json().catch(()=>({})),W=typeof Q.delivered==="number"?Q.delivered:0;return{ok:!0,tool:v,delivered:W,message:W>0?`Dispatched to ${W} open chart tab(s).`:"Dispatched, but no open Aether Wealth chart tab is listening — open the dashboard in your browser, then retry."}}function Y7($){let v=JC($);if(!v)return{content:[{type:"text",text:["The backend did not return the required idempotent submission envelope.","Do not re-propose this action. Check trade_intent_status and retry the same keyed submission only if no intent exists."
|
|
108
|
-
`)}],isError:!0};let D=v.proposal,X=XC(v.status,D?.approvalMethod??v.approvalMethod);return{content:[{type:"text",text:[`Broker submission status: ${v.status}. Intent: ${v.intentId}.`,X,"Use trade_intent_status to check whether it was approved, executed, expired, cancelled, or failed."
|
|
109
|
-
`)}],isError:v.status==="execution_pending"||v.status==="execution_unknown"||v.status==="failed"}}function I7($){let v=GC($),D=wH(v?.approvalMethod);return{content:[{type:"text",text:[`Pending broker intent created with approval method: ${v?.approvalMethod??"unknown"}.`,D,"Use trade_intent_status to check whether it was approved, executed, expired, cancelled, or failed."
|
|
110
|
-
`)}]}}function wH($){if($==="email_otp")return"Ask the user for the emailed confirmation code, then call execute_trade with this intentId and code. Never guess or fabricate the code.";if($==="app_otp")return"Ask the user for the current authenticator app code, then call execute_trade with this intentId and code. Never guess or fabricate the code.";if($==="push")return"Do NOT ask the user for a code. This action executes only when the user taps Approve in the Aether Wealth app.";if($==="auto")return"No code is required. Call execute_trade with this intentId to execute the action.";return"Inspect the returned proposal before taking the next step."}function XC($,v){if($==="executed")return"The action is already executed. Do not call execute_trade.";if($==="execution_unknown")return"Execution outcome is unknown. Inspect trade_intent_status and do not re-propose this action.";if($==="execution_pending"){if(v==="auto")return"Automatic execution remains pending. Call execute_trade with this intentId for explicit recovery; do not create a replacement proposal.";return"Execution remains pending and may be retried when the executor is available. Inspect trade_intent_status first."}if($==="pending_execution")return"This automatic connection uses the normal two-step flow. Call execute_trade with this intentId; do not create a replacement proposal.";if($==="failed")return"The action failed. Inspect trade_intent_status before proposing a new action.";return wH(v)}function GC($){if(typeof $!=="object"||$===null)return null;let v=$.proposal;if(typeof v!=="object"||v===null)return null;return v}function JC($){if(typeof $!=="object"||$===null)return null;let v=$.submission;if(typeof v!=="object"||v===null)return null;let{status:D,intentId:X,proposal:U,intent:G}=v;if(typeof D!=="string"||typeof X!=="string")return null;let J=typeof G?.approvalMethod==="string"?G.approvalMethod:void 0;return{status:D,intentId:X,...J===void 0?{}:{approvalMethod:J},...U===void 0?{}:{proposal:U}}}var lX=[E({name:"get_broker_account",description:"Get the LIVE broker account balance, equity, currency, and connection status for a connected trading account (distinct from list_accounts which lists journal accounts). Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),pZ(v.trpc,$)}}),E({name:"list_broker_positions",description:"List open broker positions with P&L. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),tZ(v.trpc,$)}}),E({name:"get_broker_quote",description:"Get the current broker-native bid and ask for a symbol through the connected execution account. For an MT5 executor connection, this reads the live tick from its VPS and rejects stale ticks. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},symbol:{type:"string",minLength:1,maxLength:10}},required:["accountId","symbol"],additionalProperties:!1},handler:async($,v)=>{return await T(v),oZ(v.trpc,$)}}),E({name:"connect_broker",description:"Attach a live execution provider to a journal account. For MetaApi, provide its account id and region. For `mt5_executor`, provide the expected MT5 login as `providerAccountRef` and its `baseUrl` under `*.aetherwealth.ai`. `providerToken` is sensitive — never expose or log it.",inputSchema:{type:"object",properties:{accountId:{type:"string"},provider:{type:"string",enum:["metaapi","mt5_executor"],description:"Defaults to 'metaapi' when omitted."},region:{type:"string",enum:["new-york","london"]},baseUrl:{type:"string",description:"Required for `mt5_executor`; the executor's HTTPS origin under `*.aetherwealth.ai`."},providerAccountRef:{type:"string",description:"The MetaApi account id, or the expected numeric MT5 login for `mt5_executor`."},providerToken:{type:"string",description:"The provider token. For `mt5_executor`, use its service bearer token. Sensitive — never expose or log it."},approvalMode:{type:"string",enum:["app_otp","email_otp"],description:"Confirmation mode. 'auto' is not selectable here; 'push' is also set later via set_broker_approval_mode because it requires an active app push subscription."}},required:["accountId","providerAccountRef","providerToken"],additionalProperties:!1},handler:async($,v)=>{return await T(v),mZ(v.trpc,$)}}),E({name:"broker_connection_status",description:"Get the current live broker connection status. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),cZ(v.trpc,$)}}),E({name:"disconnect_broker",description:"Disarm broker trading for a connected account instantly. Cancels pending intents, keeps executed history, and does not require confirmation because it is the kill switch. Reconnect later with connect_broker. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),dZ(v.trpc,$)}}),E({name:"set_broker_approval_mode",description:"Change how broker trades are confirmed for a connection between `app_otp` (authenticator), `email_otp`, and `push` (tap-to-approve in the Aether Wealth app; requires an active push subscription). The agent cannot enable `auto` here.",inputSchema:{type:"object",properties:{accountId:{type:"string"},approvalMode:{type:"string",enum:["app_otp","email_otp","push"]}},required:["accountId","approvalMode"],additionalProperties:!1},handler:async($,v)=>{return await T(v),rZ(v.trpc,$)}}),E({name:"propose_trade",description:"Size and propose a broker trade. Supplying idempotencyKey uses the auto-only keyed path; MT5 may place LIVE broker state inline, while MetaApi returns pending execution. Without a key this remains the normal two-step intent flow. Provide exactly one of riskPercent or riskAmount; entryPrice is required for limit/stop. Push-mode intents are sent to the user's device and must not be executed by the agent. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},symbol:{type:"string"},direction:{type:"string",enum:["BUY","SELL"]},orderType:{type:"string",enum:["market","limit","stop"]},entryPrice:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},riskPercent:{type:"number"},riskAmount:{type:"number"},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","symbol","direction","stopLoss"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await eZ(v.trpc,$)):I7(await aZ(v.trpc,$))}}),E({name:"execute_trade",description:"Place a previously proposed trade. Email/app modes require the user-provided code; auto mode requires no per-trade code. Push intents cannot be executed by the agent. NEVER guess or fabricate a code.",inputSchema:{type:"object",properties:{intentId:{type:"string"},code:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),sZ(v.trpc,$)}}),E({name:"trade_intent_status",description:"Check whether a proposed trade was approved, executed, expired, cancelled, or failed. Especially useful for push-mode intents, which the user approves on their device.",inputSchema:{type:"object",properties:{intentId:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),vO(v.trpc,$)}}),E({name:"cancel_trade_intent",description:"Cancel a previously proposed broker trade. Requires sign-in.",inputSchema:{type:"object",properties:{intentId:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),$O(v.trpc,$)}}),E({name:"close_position",description:"Propose closing an open broker position fully, or partially by passing `volume`. With idempotencyKey, use the auto-only keyed path; MT5 may close LIVE broker state inline, while MetaApi returns pending execution. Without a key this is the normal two-step intent flow. For email_otp/app_otp ask the user for the code and call execute_trade. For push, do not ask for a code — the user taps Approve in the app.",inputSchema:{type:"object",properties:{accountId:{type:"string"},positionId:{type:"string"},volume:{type:"number"},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","positionId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await DO(v.trpc,$)):I7(await UO(v.trpc,$))}}),E({name:"modify_position",description:"Propose adjusting stop-loss / take-profit on an open position. Stop-loss changes that INCREASE risk are rejected (use the app). With idempotencyKey, use the auto-only keyed path; MT5 may modify LIVE broker state inline, while MetaApi returns pending execution. Without a key this is the normal two-step intent flow. Pass null to clear a level. For email_otp/app_otp ask for the code and call execute_trade, while push is approved in the app.",inputSchema:{type:"object",properties:{accountId:{type:"string"},positionId:{type:"string"},stopLoss:{type:["number","null"]},takeProfit:{type:["number","null"]},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","positionId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await GO(v.trpc,$)):I7(await XO(v.trpc,$))}})],hX=[E({name:"draw_trade_on_chart",description:"Draw a proposed/open trade (entry, stop, target) on the user's OPEN Aether Wealth chart as a position shape. Switch to the intended pair/timeframe first with chart_select_pair.",inputSchema:{type:"object",properties:{direction:{type:"string",enum:["long","short"]},entry:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},label:{type:"string"}},required:["direction","entry","stopLoss"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"draw_chart_shape",{shapes:[{type:"position",direction:$.direction,entry:$.entry,stop:$.stopLoss,...$.takeProfit===void 0?{}:{target:$.takeProfit},...$.label===void 0?{}:{label:$.label}}]})}})];var QC=["news","press_release"];function WC($){return{pair:$.pair,...$.limit===void 0?{}:{limit:$.limit}}}function zC($){return{currency:$.currency,...$.limit===void 0?{}:{limit:$.limit}}}function YC($){return{currency:$.currency,...$.indicator===void 0?{}:{indicator:$.indicator},...$.limit===void 0?{}:{limit:$.limit}}}function IC($){return{...$.limit===void 0?{}:{limit:$.limit}}}function VC($){return{currency:$.currency,...$.documentType===void 0?{}:{documentType:$.documentType},...$.limit===void 0?{}:{limit:$.limit}}}var mX={type:"integer",minimum:1,maximum:250,description:"Max rows to return (1-250, default 120)."},cX=[E({name:"get_fx_pair_context",description:"Macro context for a currency PAIR (e.g. EURUSD) — the per-currency macro state for both legs (recent releases, surprises, derived trend). Use for a pair-level macro read; for one currency use get_fx_currency_context. Distinct from get_macro_series (raw calendar history) and get_macro_indicator (one curated series).",inputSchema:{type:"object",properties:{pair:{type:"string",description:"6-letter currency pair, e.g. EURUSD (case-insensitive)"},limit:mX},required:["pair"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.pairContext.query(WC($))}}),E({name:"get_fx_currency_context",description:"Macro context for a single CURRENCY (e.g. USD) — recent economic releases, surprises, and the derived macro state for that currency.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},limit:mX},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.currencyContext.query(zC($))}}),E({name:"get_fx_predictions",description:"Model forecasts / predictions for a currency's upcoming macro releases. Optionally filter to one indicator by release slug (e.g. cpi).",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},indicator:{type:"string",description:"Optional release slug filter, e.g. cpi"},limit:mX},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.predictions.query(YC($))}}),E({name:"get_fx_risk_sentiment",description:"Cross-market risk-on / risk-off sentiment series (not currency-scoped). Use to gauge broad market risk appetite.",inputSchema:{type:"object",properties:{limit:mX},additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.riskSentiment.query(IC($))}}),E({name:"get_fx_documents",description:"Source documents behind the FX macro data for a currency — news items and press releases. Filter by documentType.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},documentType:{type:"string",enum:[...QC],description:"Optional filter: news or press_release"},limit:{type:"integer",minimum:1,maximum:100,description:"Max rows to return (1-100, default 50)."}},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.documents.query(VC($))}}),E({name:"get_fx_sync_state",description:"Ingestion/sync status of the FX macro datasets (per dataset family: status, freshness, last success, next refresh). Diagnostic — check data freshness before relying on the other get_fx_* tools.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.syncState.query()}})];var KC="momentum: rsi, macd, stoch, stochf, stochrsi, cci, cmo, mom, roc, rocr, willr, mfi, ultosc, ppo, apo, bop, aroon, aroonosc; trend: adx, adxr, dx, plus_di, minus_di, plus_dm, minus_dm, sar, supertrend; moving averages: sma, ema, wma, dema, tema, trima, kama, t3ma, mama, vwap, midpoint, midprice; bands: bbands, keltner, percent_b; volatility: atr, natr, trange; volume: obv, ad, adosc; stats: linearreg, linearregslope, linearregangle, linearregintercept, stddev, var, tsf",dX=[E({name:"get_indicators",description:"Compute Twelve Data technical indicators for a pair + timeframe (requires sign-in). "+`Pass up to 8 indicators by name; params are optional and default sensibly. Names — ${KC}. Bad names/params return an error listing valid options. At most 2 distinct pairs per user at a time.`,inputSchema:{type:"object",properties:{pair:{type:"string",description:"Canonical id, e.g. EURUSD, XAUUSD, BTCUSD (case-insensitive)"},interval:{type:"string",description:"One of 1m, 5m, 15m, 30m, 45m, 1h, 2h, 4h, 1d"},indicators:{type:"array",minItems:1,maxItems:8,description:"Up to 8 indicators to compute on the pair",items:{type:"object",properties:{name:{type:"string",description:"Indicator name, e.g. rsi, macd, ema, bbands, atr, adx"},params:{type:"object",description:'Optional params, e.g. {"time_period": 14} or MACD {"fast_period":12,"slow_period":26}. Defaults applied when omitted.'}},required:["name"],additionalProperties:!1}},outputsize:{type:"integer",minimum:1,maximum:500,description:"Bars to return per indicator (default 30)"}},required:["pair","interval","indicators"],additionalProperties:!1},handler:async($,v)=>{return await T(v),JO(v.trpc,$)}})];var LC=["horizontal_line","horizontal_ray","vertical_line","cross_line","zone","rectangle","trendline","ray","right_ray","left_ray","extended_line","info_line","trend_angle","position"],rX=[E({name:"chart_select_pair",description:"Switch the user's open Aether Wealth chart to a trading pair. Use the canonical id (case-insensitive), e.g. XAUUSD for gold, EURUSD, BTCUSD. Drives the live browser chart, not backend state.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"Canonical pair id, e.g. XAUUSD (gold), EURUSD, BTCUSD"}},required:["pair"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"select_pair",{pair:$.pair})}}),E({name:"chart_change_timeframe",description:"Change the timeframe/resolution of the user's open Aether Wealth chart, e.g. 1m, 5m, 15m, 30m, 1h, 4h, 1d.",inputSchema:{type:"object",properties:{timeframe:{type:"string",description:"e.g. 1m, 5m, 15m, 30m, 1h, 4h, 1d"}},required:["timeframe"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"change_timeframe",{timeframe:$.timeframe})}}),E({name:"chart_draw_shapes",description:"Draw one or more shapes on the user's OPEN Aether Wealth chart. For key levels use multiple `horizontal_line` shapes, each with `price` and a short `label`. For a fair-value-gap / supply-demand zone use a `zone` with `priceHigh` + `priceLow`. For a trade idea use a `position` with `direction` + `entry`/`stop`/`target`. Prices are in the instrument's price units; `time1`/`time2`/`entryTime` are optional epoch seconds. Switch to the intended pair + timeframe first.",inputSchema:{type:"object",properties:{shapes:{type:"array",description:"Shapes to draw in one batch.",items:{type:"object",properties:{type:{type:"string",enum:[...LC]},label:{type:"string"},color:{type:"string",description:"hex, e.g. #22c55e"},price:{type:"number"},priceHigh:{type:"number"},priceLow:{type:"number"},price1:{type:"number"},price2:{type:"number"},time1:{type:"number",description:"epoch seconds"},time2:{type:"number",description:"epoch seconds"},direction:{type:"string",enum:["long","short"]},entry:{type:"number"},stop:{type:"number"},target:{type:"number"},entryTime:{type:"number",description:"epoch seconds"}},required:["type"],additionalProperties:!1}}},required:["shapes"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"draw_chart_shape",{shapes:$.shapes})}}),E({name:"chart_remove_drawings",description:"Remove AI-drawn shapes from the user's open Aether Wealth chart. `target` defaults to 'all' (clear everything the assistant drew); also accepts 'last', 'focused', or a specific shape id.",inputSchema:{type:"object",properties:{target:{type:"string",description:"'all' (default), 'last', 'focused', or a shape id"}},additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"remove_chart_drawings",{target:$.target??"all"})}})];var pX=[E({name:"get_candles",description:"Get OHLC candles for a pair + timeframe and optional sourceId. Omit sourceId for Twelve Data. Non-default sources return only native timeframes advertised by get_market_config; they are never re-aggregated. Returns up to 200 bars plus nextCursor. Pass nextCursor back as cursor until it is null. The legacy offset remains available for shallow pagination; do not combine offset and cursor.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"e.g. EURUSD, BTCUSD (canonical id; case-insensitive)"},timeframe:{type:"string",description:"A base (1m, 5m, 15m, 30m, 1h, 4h, 1d) or an integer multiple of one (e.g. 2h, 3h, 12h, 90m). No fractional (2.5h) or sub-minute values."},sourceId:{type:"string",pattern:"^[a-z][a-z0-9-]{0,62}$",description:"Market-data source from get_market_config, e.g. mt5-primary. Omit for Twelve Data."},limit:{type:"integer",minimum:1,maximum:200,description:"Maximum candles to return (1-200). Omit for the newest 200."},offset:{type:"integer",minimum:0,maximum:4800,description:"Legacy shallow pagination: number of recent candles to skip (0–4800). Use cursor instead for complete history; do not combine offset and cursor."},cursor:{type:"string",minLength:1,maxLength:2048,description:"Opaque nextCursor returned by the previous get_candles page. Omit on the first page; continue until nextCursor is null. Do not combine with offset."}},required:["pair","timeframe"],additionalProperties:!1},handler:async($,v)=>{return await T(v),qX(v.trpc,$)}}),E({name:"list_economic_calendar",description:'List economic calendar events. Filter by currency, date range, release, or minimum impact (1-5). Currency and indicator/release are validated against the catalog: unknown values return an error listing valid options, so an empty result always means "no events" rather than "bad input".',inputSchema:{type:"object",properties:{currency:{type:"array",items:{type:"string"},description:'ISO currency codes, e.g. ["USD","EUR"]'},from:{type:"string",description:"ISO 8601 date or datetime"},to:{type:"string",description:"ISO 8601 date or datetime"},indicator:{type:"string",description:"release slug, e.g. cpi"},impactMin:{type:"integer",minimum:1,maximum:5},view:{type:"string",enum:["minimal","rich"]}},additionalProperties:!1},handler:async($,v)=>{return await T(v),WO(v.trpc,$)}}),E({name:"get_macro_series",description:"Get a macro indicator time series (e.g. USD cpi). Currency and indicator are validated against the catalog: unknown values return an error listing valid options (case-insensitive).",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD"},indicator:{type:"string",description:"release slug, e.g. cpi"},from:{type:"string",description:"ISO 8601 date or datetime"},to:{type:"string",description:"ISO 8601 date or datetime"},limit:{type:"integer",minimum:1,maximum:1000}},required:["currency","indicator"],additionalProperties:!1},handler:async($,v)=>{return await T(v),zO(v.trpc,$)}}),E({name:"get_macro_indicator",description:"Get a curated macro indicator series from FXMacroData (live + cached) with unit/frequency metadata and the central-bank target where available — use get_macro_series for raw historical calendar values. Range defaults to 12m; returns a null result when the macro data provider is not configured.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD"},indicator:{type:"string",description:"indicator slug in lowercase snake_case, e.g. cpi"},range:{type:"string",enum:[...QO],description:"Lookback window (default 12m)"}},required:["currency","indicator"],additionalProperties:!1},handler:async($,v)=>{return await T(v),YO(v.trpc,$)}}),E({name:"get_market_config",description:"Get supported instruments, timeframes, and per-source candle/live capabilities. Use marketDataSources before selecting a non-default sourceId.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),IO(v.trpc)}})];var MH={stopLoss:{type:"number"},takeProfit:{type:"number"},lotSize:{type:"number"},notes:{type:"string"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},riskPercent:{type:"number"},tags:{type:"array",items:{type:"string"}}},BC={pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},exitPrice:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},lotSize:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},exitTime:{type:"string",description:"ISO 8601 datetime"},notes:{type:"string"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},emotionalTag:{type:"string"},fees:{type:"number"},swap:{type:"number"},reportedPnl:{type:"number",description:"Broker-reported P&L to store instead of recalculated P&L"},tags:{type:"array",items:{type:"string"}}},tX=[E({name:"list_trades",description:"List the authenticated user's trades. Filter by account, status, pair, or date range (ISO 8601). Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},status:{type:"string",enum:["OPEN","CLOSED","CANCELLED","ALL"]},pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},from:{type:"string",description:"ISO 8601 datetime (inclusive lower bound)"},to:{type:"string",description:"ISO 8601 datetime (inclusive upper bound)"},page:wO,limit:AO},additionalProperties:!1},handler:async($,v)=>{return await T(v),LO(v.trpc,$)}}),E({name:"get_trade",description:"Get a single trade by id. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),BO(v.trpc,$)}}),E({name:"list_accounts",description:"List the user's trading accounts (use an account's id when creating a trade). Requires sign-in.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),kZ(v.trpc)}}),E({name:"create_account",description:'Create a trading account in the journal. Use accountType "demo" for paper/demo accounts and "live" for live accounts. Requires sign-in.',inputSchema:{type:"object",properties:{name:{type:"string"},broker:{type:"string"},accountType:{type:"string",description:"default: personal"},initialBalance:{type:"number",description:"default: 0"},currency:{type:"string",description:"3-letter code, default: USD"},notes:{type:"string"},defaultRiskAmount:{type:"number"}},required:["name"],additionalProperties:!1},handler:async($,v)=>{return await T(v),EZ(v.trpc,$)}}),E({name:"trade_stats",description:"Trading performance statistics (win rate, PnL, etc.). Filter by account, pair, or date range. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},pair:{type:"string"},from:{type:"string",description:"ISO 8601 datetime"},to:{type:"string",description:"ISO 8601 datetime"}},additionalProperties:!1},handler:async($,v)=>{return await T(v),VO(v.trpc,$)}}),E({name:"create_trade",description:"Record a new trade. Requires sign-in. Use list_accounts to find the accountId.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id (see list_accounts)"},pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},...MH},required:["accountId","pair","direction","entryPrice","entryTime"],additionalProperties:!1},handler:async($,v)=>{return await T(v),NO(v.trpc,$)}}),E({name:"update_trade",description:"Update fields on an existing trade. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},pair:{type:"string"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},...MH},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),ZO(v.trpc,$)}}),E({name:"close_trade",description:"Close an open trade with an exit price + time. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},exitPrice:{type:"number"},exitTime:{type:"string",description:"ISO 8601 datetime"},fees:{type:"number"},swap:{type:"number"},notes:{type:"string"}},required:["id","exitPrice","exitTime"],additionalProperties:!1},handler:async($,v)=>{return await T(v),OO(v.trpc,$)}}),E({name:"delete_trade",description:"Permanently delete a single trade by id. Only trades on demo accounts can be deleted. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),HO(v.trpc,$)}}),E({name:"delete_trades",description:"Permanently delete trades on a specific demo account. Requires explicit status (OPEN, CLOSED, CANCELLED, or ALL). This cannot be undone. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id to delete from"},status:{type:"string",enum:["OPEN","CLOSED","CANCELLED","ALL"]}},required:["accountId","status"],additionalProperties:!1},handler:async($,v)=>{return await T(v),FO(v.trpc,$)}}),E({name:"import_trades",description:"Import up to 500 trades into one account. Rows with both exitPrice and exitTime are imported as closed trades. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id to import into"},trades:{type:"array",minItems:1,maxItems:500,items:{type:"object",properties:BC,required:["pair","direction","entryPrice","lotSize","entryTime"],additionalProperties:!1}}},required:["accountId","trades"],additionalProperties:!1},handler:async($,v)=>{return await T(v),jO(v.trpc,$)}})];function V7($={}){let v=$.includeAuthTools??!0,D=$.includeLiveChartTools??!1,X=$.includeRenderChartTools??!Y4;return[...v?EX:[],...nX,...tX,...lX,...kX,...pX,...cX,...dX,...X?iX:[],...D?[...rX,...hX]:[]]}var NC="aether-wealth-mcp",ZC="0.2.15";function PH($,v){let D=new A0({name:NC,version:ZC},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return OC(D,$,v??$.toolContext),D}function OC($,v,D){let X=!1,U=(J)=>{let Q=v.clientGate?.nudge;if(Q&&!X)return X=!0,`${Q}
|
|
107
|
+
Skipped: ${W.join("; ")}`:"",Y=`Rendered ${$.pair} ${$.timeframe} — ${X.length} candles, drew ${J.drawn} shape${J.drawn===1?"":"s"}. Price range ${J.priceRange.min}–${J.priceRange.max}.${z}`;return{content:[{type:"image",data:Buffer.from(J.png).toString("base64"),mimeType:"image/png"},{type:"text",text:Y}]}}})];async function yX($){try{return{ok:!0,value:await $}}catch(v){return{ok:!1,error:v instanceof Error?v.message:String(v)}}}function $C($){return/unauthor|forbidden|401|403|sign[ -]?in|token|credential/i.test($)}var vC="The Aether Wealth backend is unreachable right now. Try again shortly.",UC=_5.flatMap(($)=>$.dashboardOnly.map((v)=>`${$.name}: ${v}`)),nX=[E({name:"get_context",description:"Snapshot the user's live Aether Wealth state in one call — accounts, open trades, recent performance stats, and active alerts — plus the map of dashboard-only actions. Call this first to ground suggestions in what the user is actually doing. Requires sign-in.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await Promise.all([yX(v.trpc.trading.accounts.list.query()),yX(v.trpc.trading.trades.list.query({status:"OPEN",limit:200})),yX(v.trpc.trading.analytics.stats.query({})),yX(v.trpc.alerts.list.query({}))]);if(D.every((W)=>!W.ok)){let W=D.every((z)=>!z.ok&&$C(z.error));throw Error(W?l5:vC)}let[X,U,G,J]=D,Q=(W)=>W.ok?W.value:{error:W.error};return{accounts:Q(X),openTrades:Q(U),stats:Q(G),alerts:Q(J),dashboard:{url:f4,dashboardOnly:UC,note:`These actions are not available over MCP. When the user needs one, suggest they open the dashboard at ${f4}.`}}}})];function DC(){return{url:process.env.AETHER_APP_PUSH_URL?.trim()||"http://127.0.0.1:9003",secret:process.env.AI_LIVE_PUSH_SECRET?.trim()||void 0}}async function u6($,v,D){let{url:X,secret:U}=DC(),G=`${X.replace(/\/$/,"")}/api/ai/live/push`,J;try{J=await fetch(G,{method:"POST",headers:{"content-type":"application/json",...U?{"x-live-push-secret":U}:{}},body:JSON.stringify({userId:$,tool:v,args:D})})}catch(z){throw Error(`Could not reach the Aether Wealth app at ${G}. Is the dashboard running?`,{cause:z})}if(!J.ok)throw Error(`Live-chart push rejected (HTTP ${J.status}).`);let Q=await J.json().catch(()=>({})),W=typeof Q.delivered==="number"?Q.delivered:0;return{ok:!0,tool:v,delivered:W,message:W>0?`Dispatched to ${W} open chart tab(s).`:"Dispatched, but no open Aether Wealth chart tab is listening — open the dashboard in your browser, then retry."}}function Y7($){let v=JC($);if(!v)return{content:[{type:"text",text:["The backend did not return the required idempotent submission envelope.","Do not re-propose this action. Check trade_intent_status and retry the same keyed submission only if no intent exists."].join(`
|
|
108
|
+
`)},{type:"text",text:JSON.stringify($,null,2)}],isError:!0};let D=v.proposal,X=XC(v.status,D?.approvalMethod??v.approvalMethod);return{content:[{type:"text",text:[`Broker submission status: ${v.status}. Intent: ${v.intentId}.`,X,"Use trade_intent_status to check whether it was approved, executed, expired, cancelled, or failed."].join(`
|
|
109
|
+
`)},{type:"text",text:JSON.stringify($,null,2)}],isError:v.status==="execution_pending"||v.status==="execution_unknown"||v.status==="failed"}}function I7($){let v=GC($),D=wH(v?.approvalMethod);return{content:[{type:"text",text:[`Pending broker intent created with approval method: ${v?.approvalMethod??"unknown"}.`,D,"Use trade_intent_status to check whether it was approved, executed, expired, cancelled, or failed."].join(`
|
|
110
|
+
`)},{type:"text",text:JSON.stringify($,null,2)}]}}function wH($){if($==="email_otp")return"Ask the user for the emailed confirmation code, then call execute_trade with this intentId and code. Never guess or fabricate the code.";if($==="app_otp")return"Ask the user for the current authenticator app code, then call execute_trade with this intentId and code. Never guess or fabricate the code.";if($==="push")return"Do NOT ask the user for a code. This action executes only when the user taps Approve in the Aether Wealth app.";if($==="auto")return"No code is required. Call execute_trade with this intentId to execute the action.";return"Inspect the returned proposal before taking the next step."}function XC($,v){if($==="executed")return"The action is already executed. Do not call execute_trade.";if($==="execution_unknown")return"Execution outcome is unknown. Inspect trade_intent_status and do not re-propose this action.";if($==="execution_pending"){if(v==="auto")return"Automatic execution remains pending. Call execute_trade with this intentId for explicit recovery; do not create a replacement proposal.";return"Execution remains pending and may be retried when the executor is available. Inspect trade_intent_status first."}if($==="pending_execution")return"This automatic connection uses the normal two-step flow. Call execute_trade with this intentId; do not create a replacement proposal.";if($==="failed")return"The action failed. Inspect trade_intent_status before proposing a new action.";return wH(v)}function GC($){if(typeof $!=="object"||$===null)return null;let v=$.proposal;if(typeof v!=="object"||v===null)return null;return v}function JC($){if(typeof $!=="object"||$===null)return null;let v=$.submission;if(typeof v!=="object"||v===null)return null;let{status:D,intentId:X,proposal:U,intent:G}=v;if(typeof D!=="string"||typeof X!=="string")return null;let J=typeof G?.approvalMethod==="string"?G.approvalMethod:void 0;return{status:D,intentId:X,...J===void 0?{}:{approvalMethod:J},...U===void 0?{}:{proposal:U}}}var lX=[E({name:"get_broker_account",description:"Get the LIVE broker account balance, equity, currency, and connection status for a connected trading account (distinct from list_accounts which lists journal accounts). Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),pZ(v.trpc,$)}}),E({name:"list_broker_positions",description:"List open broker positions with P&L. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),tZ(v.trpc,$)}}),E({name:"get_broker_quote",description:"Get the current broker-native bid and ask for a symbol through the connected execution account. For an MT5 executor connection, this reads the live tick from its VPS and rejects stale ticks. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},symbol:{type:"string",minLength:1,maxLength:10}},required:["accountId","symbol"],additionalProperties:!1},handler:async($,v)=>{return await T(v),oZ(v.trpc,$)}}),E({name:"connect_broker",description:"Attach a live execution provider to a journal account. For MetaApi, provide its account id and region. For `mt5_executor`, provide the expected MT5 login as `providerAccountRef` and its `baseUrl` under `*.aetherwealth.ai`. `providerToken` is sensitive — never expose or log it.",inputSchema:{type:"object",properties:{accountId:{type:"string"},provider:{type:"string",enum:["metaapi","mt5_executor"],description:"Defaults to 'metaapi' when omitted."},region:{type:"string",enum:["new-york","london"]},baseUrl:{type:"string",description:"Required for `mt5_executor`; the executor's HTTPS origin under `*.aetherwealth.ai`."},providerAccountRef:{type:"string",description:"The MetaApi account id, or the expected numeric MT5 login for `mt5_executor`."},providerToken:{type:"string",description:"The provider token. For `mt5_executor`, use its service bearer token. Sensitive — never expose or log it."},approvalMode:{type:"string",enum:["app_otp","email_otp"],description:"Confirmation mode. 'auto' is not selectable here; 'push' is also set later via set_broker_approval_mode because it requires an active app push subscription."}},required:["accountId","providerAccountRef","providerToken"],additionalProperties:!1},handler:async($,v)=>{return await T(v),mZ(v.trpc,$)}}),E({name:"broker_connection_status",description:"Get the current live broker connection status. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),cZ(v.trpc,$)}}),E({name:"disconnect_broker",description:"Disarm broker trading for a connected account instantly. Cancels pending intents, keeps executed history, and does not require confirmation because it is the kill switch. Reconnect later with connect_broker. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),dZ(v.trpc,$)}}),E({name:"set_broker_approval_mode",description:"Change how broker trades are confirmed for a connection between `app_otp` (authenticator), `email_otp`, and `push` (tap-to-approve in the Aether Wealth app; requires an active push subscription). The agent cannot enable `auto` here.",inputSchema:{type:"object",properties:{accountId:{type:"string"},approvalMode:{type:"string",enum:["app_otp","email_otp","push"]}},required:["accountId","approvalMode"],additionalProperties:!1},handler:async($,v)=>{return await T(v),rZ(v.trpc,$)}}),E({name:"propose_trade",description:"Size and propose a broker trade. Supplying idempotencyKey uses the auto-only keyed path; MT5 may place LIVE broker state inline, while MetaApi returns pending execution. Without a key this remains the normal two-step intent flow. Provide exactly one of riskPercent or riskAmount; entryPrice is required for limit/stop. Push-mode intents are sent to the user's device and must not be executed by the agent. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},symbol:{type:"string"},direction:{type:"string",enum:["BUY","SELL"]},orderType:{type:"string",enum:["market","limit","stop"]},entryPrice:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},riskPercent:{type:"number"},riskAmount:{type:"number"},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","symbol","direction","stopLoss"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await eZ(v.trpc,$)):I7(await aZ(v.trpc,$))}}),E({name:"execute_trade",description:"Place a previously proposed trade. Email/app modes require the user-provided code; auto mode requires no per-trade code. Push intents cannot be executed by the agent. NEVER guess or fabricate a code.",inputSchema:{type:"object",properties:{intentId:{type:"string"},code:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),sZ(v.trpc,$)}}),E({name:"trade_intent_status",description:"Check whether a proposed trade was approved, executed, expired, cancelled, or failed. Especially useful for push-mode intents, which the user approves on their device.",inputSchema:{type:"object",properties:{intentId:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),vO(v.trpc,$)}}),E({name:"cancel_trade_intent",description:"Cancel a previously proposed broker trade. Requires sign-in.",inputSchema:{type:"object",properties:{intentId:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),$O(v.trpc,$)}}),E({name:"close_position",description:"Propose closing an open broker position fully, or partially by passing `volume`. With idempotencyKey, use the auto-only keyed path; MT5 may close LIVE broker state inline, while MetaApi returns pending execution. Without a key this is the normal two-step intent flow. For email_otp/app_otp ask the user for the code and call execute_trade. For push, do not ask for a code — the user taps Approve in the app.",inputSchema:{type:"object",properties:{accountId:{type:"string"},positionId:{type:"string"},volume:{type:"number"},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","positionId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await DO(v.trpc,$)):I7(await UO(v.trpc,$))}}),E({name:"modify_position",description:"Propose adjusting stop-loss / take-profit on an open position. Stop-loss changes that INCREASE risk are rejected (use the app). With idempotencyKey, use the auto-only keyed path; MT5 may modify LIVE broker state inline, while MetaApi returns pending execution. Without a key this is the normal two-step intent flow. Pass null to clear a level. For email_otp/app_otp ask for the code and call execute_trade, while push is approved in the app.",inputSchema:{type:"object",properties:{accountId:{type:"string"},positionId:{type:"string"},stopLoss:{type:["number","null"]},takeProfit:{type:["number","null"]},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","positionId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await GO(v.trpc,$)):I7(await XO(v.trpc,$))}})],hX=[E({name:"draw_trade_on_chart",description:"Draw a proposed/open trade (entry, stop, target) on the user's OPEN Aether Wealth chart as a position shape. Switch to the intended pair/timeframe first with chart_select_pair.",inputSchema:{type:"object",properties:{direction:{type:"string",enum:["long","short"]},entry:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},label:{type:"string"}},required:["direction","entry","stopLoss"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"draw_chart_shape",{shapes:[{type:"position",direction:$.direction,entry:$.entry,stop:$.stopLoss,...$.takeProfit===void 0?{}:{target:$.takeProfit},...$.label===void 0?{}:{label:$.label}}]})}})];var QC=["news","press_release"];function WC($){return{pair:$.pair,...$.limit===void 0?{}:{limit:$.limit}}}function zC($){return{currency:$.currency,...$.limit===void 0?{}:{limit:$.limit}}}function YC($){return{currency:$.currency,...$.indicator===void 0?{}:{indicator:$.indicator},...$.limit===void 0?{}:{limit:$.limit}}}function IC($){return{...$.limit===void 0?{}:{limit:$.limit}}}function VC($){return{currency:$.currency,...$.documentType===void 0?{}:{documentType:$.documentType},...$.limit===void 0?{}:{limit:$.limit}}}var mX={type:"integer",minimum:1,maximum:250,description:"Max rows to return (1-250, default 120)."},cX=[E({name:"get_fx_pair_context",description:"Macro context for a currency PAIR (e.g. EURUSD) — the per-currency macro state for both legs (recent releases, surprises, derived trend). Use for a pair-level macro read; for one currency use get_fx_currency_context. Distinct from get_macro_series (raw calendar history) and get_macro_indicator (one curated series).",inputSchema:{type:"object",properties:{pair:{type:"string",description:"6-letter currency pair, e.g. EURUSD (case-insensitive)"},limit:mX},required:["pair"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.pairContext.query(WC($))}}),E({name:"get_fx_currency_context",description:"Macro context for a single CURRENCY (e.g. USD) — recent economic releases, surprises, and the derived macro state for that currency.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},limit:mX},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.currencyContext.query(zC($))}}),E({name:"get_fx_predictions",description:"Model forecasts / predictions for a currency's upcoming macro releases. Optionally filter to one indicator by release slug (e.g. cpi).",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},indicator:{type:"string",description:"Optional release slug filter, e.g. cpi"},limit:mX},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.predictions.query(YC($))}}),E({name:"get_fx_risk_sentiment",description:"Cross-market risk-on / risk-off sentiment series (not currency-scoped). Use to gauge broad market risk appetite.",inputSchema:{type:"object",properties:{limit:mX},additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.riskSentiment.query(IC($))}}),E({name:"get_fx_documents",description:"Source documents behind the FX macro data for a currency — news items and press releases. Filter by documentType.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},documentType:{type:"string",enum:[...QC],description:"Optional filter: news or press_release"},limit:{type:"integer",minimum:1,maximum:100,description:"Max rows to return (1-100, default 50)."}},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.documents.query(VC($))}}),E({name:"get_fx_sync_state",description:"Ingestion/sync status of the FX macro datasets (per dataset family: status, freshness, last success, next refresh). Diagnostic — check data freshness before relying on the other get_fx_* tools.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.syncState.query()}})];var KC="momentum: rsi, macd, stoch, stochf, stochrsi, cci, cmo, mom, roc, rocr, willr, mfi, ultosc, ppo, apo, bop, aroon, aroonosc; trend: adx, adxr, dx, plus_di, minus_di, plus_dm, minus_dm, sar, supertrend; moving averages: sma, ema, wma, dema, tema, trima, kama, t3ma, mama, vwap, midpoint, midprice; bands: bbands, keltner, percent_b; volatility: atr, natr, trange; volume: obv, ad, adosc; stats: linearreg, linearregslope, linearregangle, linearregintercept, stddev, var, tsf",dX=[E({name:"get_indicators",description:"Compute Twelve Data technical indicators for a pair + timeframe (requires sign-in). "+`Pass up to 8 indicators by name; params are optional and default sensibly. Names — ${KC}. Bad names/params return an error listing valid options. At most 2 distinct pairs per user at a time.`,inputSchema:{type:"object",properties:{pair:{type:"string",description:"Canonical id, e.g. EURUSD, XAUUSD, BTCUSD (case-insensitive)"},interval:{type:"string",description:"One of 1m, 5m, 15m, 30m, 45m, 1h, 2h, 4h, 1d"},indicators:{type:"array",minItems:1,maxItems:8,description:"Up to 8 indicators to compute on the pair",items:{type:"object",properties:{name:{type:"string",description:"Indicator name, e.g. rsi, macd, ema, bbands, atr, adx"},params:{type:"object",description:'Optional params, e.g. {"time_period": 14} or MACD {"fast_period":12,"slow_period":26}. Defaults applied when omitted.'}},required:["name"],additionalProperties:!1}},outputsize:{type:"integer",minimum:1,maximum:500,description:"Bars to return per indicator (default 30)"}},required:["pair","interval","indicators"],additionalProperties:!1},handler:async($,v)=>{return await T(v),JO(v.trpc,$)}})];var LC=["horizontal_line","horizontal_ray","vertical_line","cross_line","zone","rectangle","trendline","ray","right_ray","left_ray","extended_line","info_line","trend_angle","position"],rX=[E({name:"chart_select_pair",description:"Switch the user's open Aether Wealth chart to a trading pair. Use the canonical id (case-insensitive), e.g. XAUUSD for gold, EURUSD, BTCUSD. Drives the live browser chart, not backend state.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"Canonical pair id, e.g. XAUUSD (gold), EURUSD, BTCUSD"}},required:["pair"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"select_pair",{pair:$.pair})}}),E({name:"chart_change_timeframe",description:"Change the timeframe/resolution of the user's open Aether Wealth chart, e.g. 1m, 5m, 15m, 30m, 1h, 4h, 1d.",inputSchema:{type:"object",properties:{timeframe:{type:"string",description:"e.g. 1m, 5m, 15m, 30m, 1h, 4h, 1d"}},required:["timeframe"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"change_timeframe",{timeframe:$.timeframe})}}),E({name:"chart_draw_shapes",description:"Draw one or more shapes on the user's OPEN Aether Wealth chart. For key levels use multiple `horizontal_line` shapes, each with `price` and a short `label`. For a fair-value-gap / supply-demand zone use a `zone` with `priceHigh` + `priceLow`. For a trade idea use a `position` with `direction` + `entry`/`stop`/`target`. Prices are in the instrument's price units; `time1`/`time2`/`entryTime` are optional epoch seconds. Switch to the intended pair + timeframe first.",inputSchema:{type:"object",properties:{shapes:{type:"array",description:"Shapes to draw in one batch.",items:{type:"object",properties:{type:{type:"string",enum:[...LC]},label:{type:"string"},color:{type:"string",description:"hex, e.g. #22c55e"},price:{type:"number"},priceHigh:{type:"number"},priceLow:{type:"number"},price1:{type:"number"},price2:{type:"number"},time1:{type:"number",description:"epoch seconds"},time2:{type:"number",description:"epoch seconds"},direction:{type:"string",enum:["long","short"]},entry:{type:"number"},stop:{type:"number"},target:{type:"number"},entryTime:{type:"number",description:"epoch seconds"}},required:["type"],additionalProperties:!1}}},required:["shapes"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"draw_chart_shape",{shapes:$.shapes})}}),E({name:"chart_remove_drawings",description:"Remove AI-drawn shapes from the user's open Aether Wealth chart. `target` defaults to 'all' (clear everything the assistant drew); also accepts 'last', 'focused', or a specific shape id.",inputSchema:{type:"object",properties:{target:{type:"string",description:"'all' (default), 'last', 'focused', or a shape id"}},additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"remove_chart_drawings",{target:$.target??"all"})}})];var pX=[E({name:"get_candles",description:"Get OHLC candles for a pair + timeframe and optional sourceId. Omit sourceId for Twelve Data. Non-default sources return only native timeframes advertised by get_market_config; they are never re-aggregated. Returns up to 200 bars plus nextCursor. Pass nextCursor back as cursor until it is null. The legacy offset remains available for shallow pagination; do not combine offset and cursor.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"e.g. EURUSD, BTCUSD (canonical id; case-insensitive)"},timeframe:{type:"string",description:"A base (1m, 5m, 15m, 30m, 1h, 4h, 1d) or an integer multiple of one (e.g. 2h, 3h, 12h, 90m). No fractional (2.5h) or sub-minute values."},sourceId:{type:"string",pattern:"^[a-z][a-z0-9-]{0,62}$",description:"Market-data source from get_market_config, e.g. mt5-primary. Omit for Twelve Data."},limit:{type:"integer",minimum:1,maximum:200,description:"Maximum candles to return (1-200). Omit for the newest 200."},offset:{type:"integer",minimum:0,maximum:4800,description:"Legacy shallow pagination: number of recent candles to skip (0–4800). Use cursor instead for complete history; do not combine offset and cursor."},cursor:{type:"string",minLength:1,maxLength:2048,description:"Opaque nextCursor returned by the previous get_candles page. Omit on the first page; continue until nextCursor is null. Do not combine with offset."}},required:["pair","timeframe"],additionalProperties:!1},handler:async($,v)=>{return await T(v),qX(v.trpc,$)}}),E({name:"list_economic_calendar",description:'List economic calendar events. Filter by currency, date range, release, or minimum impact (1-5). Currency and indicator/release are validated against the catalog: unknown values return an error listing valid options, so an empty result always means "no events" rather than "bad input".',inputSchema:{type:"object",properties:{currency:{type:"array",items:{type:"string"},description:'ISO currency codes, e.g. ["USD","EUR"]'},from:{type:"string",description:"ISO 8601 date or datetime"},to:{type:"string",description:"ISO 8601 date or datetime"},indicator:{type:"string",description:"release slug, e.g. cpi"},impactMin:{type:"integer",minimum:1,maximum:5},view:{type:"string",enum:["minimal","rich"]}},additionalProperties:!1},handler:async($,v)=>{return await T(v),WO(v.trpc,$)}}),E({name:"get_macro_series",description:"Get a macro indicator time series (e.g. USD cpi). Currency and indicator are validated against the catalog: unknown values return an error listing valid options (case-insensitive).",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD"},indicator:{type:"string",description:"release slug, e.g. cpi"},from:{type:"string",description:"ISO 8601 date or datetime"},to:{type:"string",description:"ISO 8601 date or datetime"},limit:{type:"integer",minimum:1,maximum:1000}},required:["currency","indicator"],additionalProperties:!1},handler:async($,v)=>{return await T(v),zO(v.trpc,$)}}),E({name:"get_macro_indicator",description:"Get a curated macro indicator series from FXMacroData (live + cached) with unit/frequency metadata and the central-bank target where available — use get_macro_series for raw historical calendar values. Range defaults to 12m; returns a null result when the macro data provider is not configured.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD"},indicator:{type:"string",description:"indicator slug in lowercase snake_case, e.g. cpi"},range:{type:"string",enum:[...QO],description:"Lookback window (default 12m)"}},required:["currency","indicator"],additionalProperties:!1},handler:async($,v)=>{return await T(v),YO(v.trpc,$)}}),E({name:"get_market_config",description:"Get supported instruments, timeframes, and per-source candle/live capabilities. Use marketDataSources before selecting a non-default sourceId.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),IO(v.trpc)}})];var MH={stopLoss:{type:"number"},takeProfit:{type:"number"},lotSize:{type:"number"},notes:{type:"string"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},riskPercent:{type:"number"},tags:{type:"array",items:{type:"string"}}},BC={pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},exitPrice:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},lotSize:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},exitTime:{type:"string",description:"ISO 8601 datetime"},notes:{type:"string"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},emotionalTag:{type:"string"},fees:{type:"number"},swap:{type:"number"},reportedPnl:{type:"number",description:"Broker-reported P&L to store instead of recalculated P&L"},tags:{type:"array",items:{type:"string"}}},tX=[E({name:"list_trades",description:"List the authenticated user's trades. Filter by account, status, pair, or date range (ISO 8601). Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},status:{type:"string",enum:["OPEN","CLOSED","CANCELLED","ALL"]},pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},from:{type:"string",description:"ISO 8601 datetime (inclusive lower bound)"},to:{type:"string",description:"ISO 8601 datetime (inclusive upper bound)"},page:wO,limit:AO},additionalProperties:!1},handler:async($,v)=>{return await T(v),LO(v.trpc,$)}}),E({name:"get_trade",description:"Get a single trade by id. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),BO(v.trpc,$)}}),E({name:"list_accounts",description:"List the user's trading accounts (use an account's id when creating a trade). Requires sign-in.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),kZ(v.trpc)}}),E({name:"create_account",description:'Create a trading account in the journal. Use accountType "demo" for paper/demo accounts and "live" for live accounts. Requires sign-in.',inputSchema:{type:"object",properties:{name:{type:"string"},broker:{type:"string"},accountType:{type:"string",description:"default: personal"},initialBalance:{type:"number",description:"default: 0"},currency:{type:"string",description:"3-letter code, default: USD"},notes:{type:"string"},defaultRiskAmount:{type:"number"}},required:["name"],additionalProperties:!1},handler:async($,v)=>{return await T(v),EZ(v.trpc,$)}}),E({name:"trade_stats",description:"Trading performance statistics (win rate, PnL, etc.). Filter by account, pair, or date range. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},pair:{type:"string"},from:{type:"string",description:"ISO 8601 datetime"},to:{type:"string",description:"ISO 8601 datetime"}},additionalProperties:!1},handler:async($,v)=>{return await T(v),VO(v.trpc,$)}}),E({name:"create_trade",description:"Record a new trade. Requires sign-in. Use list_accounts to find the accountId.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id (see list_accounts)"},pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},...MH},required:["accountId","pair","direction","entryPrice","entryTime"],additionalProperties:!1},handler:async($,v)=>{return await T(v),NO(v.trpc,$)}}),E({name:"update_trade",description:"Update fields on an existing trade. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},pair:{type:"string"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},...MH},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),ZO(v.trpc,$)}}),E({name:"close_trade",description:"Close an open trade with an exit price + time. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},exitPrice:{type:"number"},exitTime:{type:"string",description:"ISO 8601 datetime"},fees:{type:"number"},swap:{type:"number"},notes:{type:"string"}},required:["id","exitPrice","exitTime"],additionalProperties:!1},handler:async($,v)=>{return await T(v),OO(v.trpc,$)}}),E({name:"delete_trade",description:"Permanently delete a single trade by id. Only trades on demo accounts can be deleted. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),HO(v.trpc,$)}}),E({name:"delete_trades",description:"Permanently delete trades on a specific demo account. Requires explicit status (OPEN, CLOSED, CANCELLED, or ALL). This cannot be undone. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id to delete from"},status:{type:"string",enum:["OPEN","CLOSED","CANCELLED","ALL"]}},required:["accountId","status"],additionalProperties:!1},handler:async($,v)=>{return await T(v),FO(v.trpc,$)}}),E({name:"import_trades",description:"Import up to 500 trades into one account. Rows with both exitPrice and exitTime are imported as closed trades. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id to import into"},trades:{type:"array",minItems:1,maxItems:500,items:{type:"object",properties:BC,required:["pair","direction","entryPrice","lotSize","entryTime"],additionalProperties:!1}}},required:["accountId","trades"],additionalProperties:!1},handler:async($,v)=>{return await T(v),jO(v.trpc,$)}})];function V7($={}){let v=$.includeAuthTools??!0,D=$.includeLiveChartTools??!1,X=$.includeRenderChartTools??!Y4;return[...v?EX:[],...nX,...tX,...lX,...kX,...pX,...cX,...dX,...X?iX:[],...D?[...rX,...hX]:[]]}var NC="aether-wealth-mcp",ZC="0.2.16";function PH($,v){let D=new A0({name:NC,version:ZC},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return OC(D,$,v??$.toolContext),D}function OC($,v,D){let X=!1,U=(J)=>{let Q=v.clientGate?.nudge;if(Q&&!X)return X=!0,`${Q}
|
|
111
111
|
|
|
112
112
|
${J}`;return J},G=(J)=>{let Q=v.clientGate?.nudge;if(!Q||X)return J;X=!0;let W=J.find((z)=>z.type==="text");if(W)return J.map((z)=>z===W?{type:"text",text:`${Q}
|
|
113
113
|
|
|
@@ -116,7 +116,7 @@ ${W.text}`}:z);return[{type:"text",text:Q},...J]};$.setRequestHandler(tv,async()
|
|
|
116
116
|
`),A1(500,{error:"internal error"})}finally{await G.close().catch(qH),await U.close().catch(qH)}}function PC($){let{servedPaths:v,bundle:D,log:X,oauth:U,bearerToken:G}=$,J=new Set([qz,...[...v].filter((W)=>W!==K7).map((W)=>`${qz}${W}`)]),Q=async(W)=>{let z=new URL(W.url),Y=bC(W,z,v,U,J);if(Y)return Y;if(U)return qC(W,U,D,X);if(G&&!xC(W,G))return new Response("unauthorized",{status:401});return SH(W,D,void 0,X)};return U?async(W)=>xV(await Q(W)):Q}function bC($,v,D,X,U){if(v.pathname===MC&&$.method==="GET")return A1(200,{ok:!0});if(X){let G=CV($,v,X,U);if(G)return G}if(!D.has(v.pathname))return new Response("not found",{status:404});if($.method==="GET"&&!X)return A1(200,{ok:!0,transport:"streamable-http"});return null}async function qC($,v,D,X){let U=uV($),G=new URL($.url).pathname,J=G===K7?"":G;for(let z of fV($,U)){let Y=v.rateLimiter.check(z);if(!Y.allowed)return new Response(JSON.stringify({error:"rate_limited"}),{status:429,headers:{"content-type":"application/json","retry-after":String(Y.retryAfterSeconds)}})}if(!U)return Sz(v,void 0,J);let Q;try{Q=await v.tokenVerifier.verify(U)}catch(z){return X(`[http] token verification unavailable: ${B7(z)}
|
|
117
117
|
`),A1(502,{error:"token verification unavailable"})}if(!Q.ok)return Sz(v,"invalid_token",J);let W=iV({token:U,userId:Q.userId,trpcUrl:v.trpcUrl,onUnauthorized:()=>v.tokenVerifier.invalidate(U)});return SH($,D,W,X)}function SC($,v,D){let U=globalThis.Bun.serve({port:v.port,hostname:D,fetch:$});return{port:U.port??v.port,close:async()=>{await U.stop()}}}async function kC($,v,D){let X=AC((J,Q)=>{EC(J,Q,$).catch(()=>{})});return await new Promise((J,Q)=>{X.once("error",Q),X.listen(v.port,D,()=>{X.off("error",Q),J()})}),{port:X.address()?.port??v.port,close:()=>new Promise((J,Q)=>{X.close((W)=>{if(W)Q(W);else J()})})}}async function EC($,v,D){try{let X=await TC($),U=await D(X);await uC(U,v)}catch(X){if(!v.headersSent){v.statusCode=500,v.setHeader("content-type","application/json"),v.end(JSON.stringify({error:B7(X)}));return}v.end()}}async function TC($){let v=RC($),D=gC($),X=$.method??"GET",U={method:X,headers:D},G=await CC($,X);if(G)U.body=G,U.duplex="half";return new Request(v,U)}function RC($){let v=$.socket,D=$.headers["x-forwarded-proto"]??(v?.encrypted?"https":"http"),X=$.headers.host??"localhost";return new URL($.url??"/",`${D}://${X}`).toString()}function gC($){let v=new Headers;for(let[D,X]of Object.entries($.headers)){if(X===void 0)continue;if(Array.isArray(X))for(let U of X)v.append(D,U);else v.append(D,X)}return v}async function CC($,v){if(v==="GET"||v==="HEAD")return;let D=[];for await(let X of $)D.push(Buffer.isBuffer(X)?X:Buffer.from(X));return D.length>0?Buffer.concat(D):void 0}async function uC($,v){if(v.statusCode=$.status,$.headers.forEach((X,U)=>{v.setHeader(U,X)}),!$.body){v.end();return}let D=$.body.getReader();try{while(!0){let{value:X,done:U}=await D.read();if(U)break;if(X)v.write(Buffer.from(X))}}finally{D.releaseLock(),v.end()}}function xC($,v){let D=$.headers.get("authorization");if(!D)return!1;if(!D.startsWith("Bearer "))return!1;let X=D.slice(7),U=Buffer.from(X,"utf8"),G=Buffer.from(v,"utf8");if(U.length!==G.length){let J=Buffer.alloc(G.length);return U.copy(J,0,0,Math.min(U.length,G.length)),bH(J,G),!1}return bH(U,G)}function B7($){if($ instanceof Error)return $.message;return String($)}function qH(){return}var fC="aether-wealth-mcp",_C=[33417,33418,33419],iC="/callback",yC=["trades:read","trades:write","accounts:read","alerts:read","alerts:write"],nC="aether-wealth-mcp",lC="AETHER_MCP_CREDENTIALS_FILE",l1={clientId:fC,redirectPorts:_C,redirectPath:iC,scopes:yC,keychainService:nC,credentialsFilePath:k0("aether-wealth-mcp"),forceFileEnvVar:lC};function kH($={}){let v=$.hosted??!1,D=v?["Authentication is handled by the MCP connection itself — there are no sign-in tools to call. If calls fail with an authorization error, the user must re-authorize this connection in their MCP client settings."]:["Auth tools:"," • login — sign in. Opens the browser; the user authenticates and approves access. Call this first if a data tool reports the user is not signed in."," • logout — sign out on this device."],X=v?" 3. If it fails with an authorization error, tell the user to re-authorize the Aether Wealth connection in their MCP client, then retry.":' 3. If it reports "not signed in", call login, wait for the user to finish in the browser, then retry.';return[{name:"getting_started",description:"Brief the LLM on what the Aether Wealth MCP exposes and the sign-in-first flow.",handler:()=>[{role:"user",content:{type:"text",text:["You are connected to the Aether Wealth MCP — programmatic access to the user's trading journal, alerts, market data, indicators, and charts over an authenticated connection.","",...D,"","Context:"," • get_context — snapshot accounts, open trades, recent stats, and active alerts in one call. Call it first to ground your suggestions.","","Domains (partial — see the aetherwealth://capabilities resource for the full set):"," • Journal: list_trades, get_trade, list_accounts, create_account, create_trade, update_trade, close_trade, delete_trade, delete_trades, import_trades, trade_stats"," • Alerts: list_alerts, create_price_alert, create_trendline_alert, create_indicator_alert, list_indicator_alerts, update_indicator_alert, delete_indicator_alert, update_alert, delete_alert"," • Market data + indicators: get_candles, get_market_config, get_indicators"," • Macro: list_economic_calendar, get_macro_series, get_macro_indicator"," • FX macro context: get_fx_pair_context, get_fx_currency_context, get_fx_predictions, get_fx_risk_sentiment, get_fx_documents, get_fx_sync_state",...Y4?[]:[" • Charts: render_chart"]," • Broker execution: propose_trade, close_position, and modify_position create intents. Email/app modes need a user-provided code; auto mode needs no per-trade code. For push intents, do not ask for a code — use trade_intent_status to check the app approval outcome. disconnect_broker is the instant kill switch and requires no confirmation.","","Deletion rule:"," • delete_trade and delete_trades are permanent and only work on demo accounts. Live-account trade deletes are blocked by the backend.","","Recommended flow:"," 1. Call get_context to load live state."," 2. Try the data tool the user asked for (e.g. list_trades).",X].join(`
|
|
118
118
|
`)}}]},{name:"dashboard_context",description:"Brief the LLM on the full Aether Wealth surface, grounding in live state, and when to suggest the dashboard.",handler:()=>[{role:"user",content:{type:"text",text:["You are connected to Aether Wealth, a multi-market trading workspace.",PX,"","Before you advise, call get_context to load the user's live state (accounts, open trades, recent stats, active alerts) so your suggestions fit what they are actually doing. The aetherwealth://capabilities resource, if your client attaches it, is the full surface map — which actions are MCP-callable vs dashboard-only.","","When the user needs something that is dashboard-only (the full charting workspace and saved layouts, the Pine editor, the full journal UI, the Aether Intelligence assistant),",`tell them to open the dashboard at ${f4} rather than attempting it over MCP.`].join(`
|
|
119
|
-
`)}}]}]}var EH="aether-wealth-mcp",N7="0.2.
|
|
119
|
+
`)}}]}]}var EH="aether-wealth-mcp",N7="0.2.16";function hC($){return{toolContext:$.toolContext,tools:new Map(($.tools??V7()).map((v)=>[v.name,v])),resources:new Map(($.resources??y5()).map((v)=>[v.uri,v])),prompts:new Map(($.prompts??i5()).map((v)=>[v.name,v])),...$.clientGate?{clientGate:$.clientGate}:{},log:$.log??((v)=>{process.stderr.write(v)})}}function mC($,v){let D=new A0({name:EH,version:N7},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return cC(D,$,v??$.toolContext),D}class oX{server;bundle;constructor($){this.bundle=hC($),this.server=mC(this.bundle),this.server.oninitialized=()=>{this.bundle.log(`${EH} initialized
|
|
120
120
|
`)}}async connect($){await this.server.connect($)}async close(){await this.server.close()}isInitialized(){return this.server.getClientVersion()!==void 0}getRegistryBundle(){return this.bundle}}function cC($,v,D){let X=!1,U=(J)=>{let Q=v.clientGate?.nudge;if(Q&&!X)return X=!0,`${Q}
|
|
121
121
|
|
|
122
122
|
${J}`;return J},G=(J)=>{let Q=v.clientGate?.nudge;if(!Q||X)return J;X=!0;let W=J.find((z)=>z.type==="text");if(W)return J.map((z)=>z===W?{type:"text",text:`${Q}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aetherwealth/mcp",
|
|
3
3
|
"mcpName": "ai.aetherwealth/mcp",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.16",
|
|
5
5
|
"description": "Official Aether Wealth MCP server for journals, alerts, forex and crypto market context, and approval-governed broker actions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|