EmidLabs Backtest
此 MCP 可以做什么
Submits cryptocurrency trading strategies for historical backtesting across assets and returns aggregate performance and trade-level results.
工具
输入模式
{'type': 'object', 'properties': {'page': {'type': 'integer', 'default': 1, 'description': '1-based page number. Default 1.'}, 'batchId': {'type': ['string', 'null'], 'default': None, 'description': 'The batchId returned by submit_backtest_batch.'}, 'pageSize': {'type': 'integer', 'default': 20, 'description': 'Items per page, 1-100. Default 20.'}, 'pollTimeoutMs': {'type': 'integer', 'default': 300000, 'description': "Defaults to 300000 (5 minutes) — a batch's slowest item determines the total wait, so this is higher than get_backtest_result's default."}, 'backtestApiKey': {'type': ['string', 'null'], 'default': None, 'description': "Your EmidLabs backtest API key. Not needed if this connector was added with a static 'x-api-key' header."}, 'backtestBaseUrl': {'type': ['string', 'null'], 'default': None, 'description': 'Defaults to the public production API.'}, 'waitForCompletion': {'type': 'boolean', 'default': True, 'description': 'If true (default), polls internally until every item in the batch is done or pollTimeoutMs elapses.'}}}
输出模式
{'type': 'object', 'properties': {'page': {'type': 'integer'}, 'items': {'type': 'array', 'items': {'type': 'object', 'properties': {'id': {'type': 'string', 'description': 'Never null here — an asset pair that failed at submission never became a real backtest record, so it never appears in this list. Check the original submit_backtest_batch response for submission-time failures.'}, 'result': {'type': ['object', 'null'], 'properties': {'pnlR': {'type': 'number', 'description': "Net profit/loss in R-units (risk multiples) — sum of every trade's PnL. This is the headline result."}, 'wins': {'type': 'integer'}, 'losses': {'type': 'integer'}, 'trades': {'type': 'integer', 'description': 'Total number of closed trades.'}, 'avgWinR': {'type': 'number', 'description': 'Average R on winning trades.'}, 'bothHit': {'type': 'integer', 'description': "Trades where both stop-loss and take-profit were hit on the same candle (resolved as stop-loss). A high BothHit relative to Trades means many trades' outcome was decided by the engine's stop-wins-ties precedence rule rather than real intracandle price path data — treat results with more skepticism the higher this ratio is."}, 'winRate': {'type': 'number', 'description': 'wins / trades. Range 0–1.'}, 'avgLossR': {'type': 'number', 'description': 'Average R on losing trades (-1.0 with the default riskManagement; varies if the strategy overrides riskManagement.stopLoss).'}, 'direction': {'type': 'string', 'description': '"long" or "short" — the direction the whole backtest traded.'}, 'totalFeeR': {'type': 'number', 'description': "Total R subtracted across all trades by configuration.entryFeePct/exitFeePct (0 if neither was set on the request). expectancyR/pnlR above are already net of this — TotalFeeR is just how much fees cost, for diagnostics. Caveat: a trade's fee-in-R cost scales inversely with that trade's own stop distance, so once fees are applied, expectancyR is only a fair comparison WITHIN one archetype's own stop convention — use pnlPct-based metrics (see get_backtest_trades) for comparisons across strategies/timeframes with different typical stop widths."}, 'grossLossR': {'type': 'number', 'description': 'Sum of all losing trades, in R-units (negative).'}, 'expectancyR': {'type': 'number', 'description': 'Average expected R per trade — (winRate * avgWinR) + (lossRate * avgLossR). This is the metric to optimize a strategy on, not raw winRate or trade count: a low win rate with a high avgWinR/avgLossR ratio can still have a strongly positive expectancyR.'}, 'grossProfitR': {'type': 'number', 'description': 'Sum of all winning trades, in R-units.'}, 'maxDrawdownR': {'type': 'number', 'description': 'Worst peak-to-trough dip across closed trades, in R-units. 0 if equity never fell below its running high-water mark. Historical/all-time — see CurrentDrawdownR for where the equity curve sits right now.'}, 'profitFactor': {'type': ['number', 'null'], 'description': 'grossProfitR / abs(grossLossR). Greater than 1 means profitable. Null when there are no losing trades — the ratio is undefined (division by zero), not infinite.'}, 'currentDrawdownR': {'type': 'number', 'description': "How far below its own peak the equity curve sits at the end of the backtest window, in R-units. 0 if the backtest ends at a new high. Includes any still-open position's unrealized PnL (see UnrealizedPnlRAtEnd) — a slump caused by an open, underwater position at window end shows up here."}, 'openPositionsAtEnd': {'type': 'integer', 'description': "Number of positions still open (never hit stop/take/exit-signal) when the backtest's date range ended. 0 in the common case. Check this before trusting CurrentDrawdownR/UnrealizedPnlRAtEnd at face value."}, 'unrealizedPnlRAtEnd': {'type': 'number', 'description': "Sum of unrealized PnL, in R-units, across all positions still open at window end — marked to market against the last available candle's close. 0 when OpenPositionsAtEnd is 0. Does NOT include exit fee (the position hasn't closed, so none has been paid) — a slight overestimate of true current drawdown when a position is open. This value feeds ONLY CurrentDrawdownR/MaxDrawdownR — it is never included in PnlR/ExpectancyR/Trades/Wins/Losses or any other metric describing closed, realized trades."}, 'scoreDistributionPct': {'type': 'object', 'description': 'For each possible total score value: fraction of candles that summed to it.', 'additionalProperties': {'type': 'number'}}, 'confirmedSignalsCount': {'type': 'integer', 'description': 'Only meaningful when the request declared confirmationSources — how many candidates cleared confirmation and became one of the Trades above.'}, 'unconfirmedSignalsCount': {'type': 'integer', 'description': "Only meaningful when the request declared confirmationSources — how many candidates were dropped before trade simulation because they weren't corroborated. ConfirmedSignalsCount + UnconfirmedSignalsCount is the total candidate count, same as what a backtest without confirmationSources would have produced."}, 'conditionsDistributionPct': {'type': 'object', 'description': 'For each possible count of simultaneously-true conditions (0, 1, 2...N): fraction of all candles where exactly that many were true at once. A strategy-tuning diagnostic (how selective is the entry setup), not a performance metric.', 'additionalProperties': {'type': 'number'}}}}, 'status': {'type': 'string'}, 'assetPair': {'type': 'string'}, 'runtimeMs': {'type': ['integer', 'null'], 'description': 'How long the analyser actually took to run this item, in milliseconds — pure compute time. Null until Status is "Completed".'}, 'recentAvgPnlR': {'type': ['number', 'null'], 'description': 'Average pnlR of the most recent RecentTradeCount closed trades.'}, 'unitsConsumed': {'type': ['number', 'null'], 'description': 'The actual execution-unit cost of this item, matching what\'s debited from the account\'s plan balance (CandlesProcessed normalized by the account\'s candles-per-unit rate). Null until Status is "Completed".'}, 'recentOutcomes': {'type': ['array', 'null'], 'items': {'type': 'string'}, 'description': '"Win"/"Loss" per recent trade, chronological — oldest first, last element is the most recent trade.'}, 'candlesProcessed': {'type': ['integer', 'null'], 'description': 'Number of candles the analyser processed for this item. Null until Status is "Completed". This is raw volume, not the plan\'s billing unit — see UnitsConsumed for that.'}, 'recentTradeCount': {'type': ['integer', 'null'], 'description': 'Number of trades RecentAvgPnlR/RecentOutcomes are based on (up to 5). Null until Status is "Completed".'}}}}, 'batchId': {'type': 'string'}, 'pageSize': {'type': 'integer'}, 'totalCount': {'type': 'integer', 'description': 'Total items in this batch, across every page — not just this one.'}, 'totalPages': {'type': 'integer'}, 'failedCount': {'type': 'integer', 'description': 'How many items finished unsuccessfully (Failed, Cancelled, or Expired).'}, 'pendingCount': {'type': 'integer', 'description': 'How many items are still Queued or Running. The whole batch is done once this reaches 0 — true on every page, not just the last.'}, 'completedCount': {'type': 'integer', 'description': 'How many items finished successfully.'}}}
输入模式
{'type': 'object', 'properties': {'id': {'type': ['string', 'null'], 'default': None, 'description': 'The id returned by submit_backtest.'}, 'pollTimeoutMs': {'type': 'integer', 'default': 120000, 'description': 'Defaults to 120000 (2 minutes).'}, 'backtestApiKey': {'type': ['string', 'null'], 'default': None, 'description': "Your EmidLabs backtest API key. Not needed if this connector was added with a static 'x-api-key' header."}, 'backtestBaseUrl': {'type': ['string', 'null'], 'default': None, 'description': 'Defaults to the public production API.'}, 'waitForCompletion': {'type': 'boolean', 'default': True, 'description': 'If true (default), polls internally until the backtest finishes or pollTimeoutMs elapses.'}}}
输出模式
{'type': 'object', 'properties': {'id': {'type': 'string'}, 'result': {'type': ['object', 'null'], 'properties': {'pnlR': {'type': 'number', 'description': "Net profit/loss in R-units (risk multiples) — sum of every trade's PnL. This is the headline result."}, 'wins': {'type': 'integer'}, 'losses': {'type': 'integer'}, 'trades': {'type': 'integer', 'description': 'Total number of closed trades.'}, 'avgWinR': {'type': 'number', 'description': 'Average R on winning trades.'}, 'bothHit': {'type': 'integer', 'description': "Trades where both stop-loss and take-profit were hit on the same candle (resolved as stop-loss). A high BothHit relative to Trades means many trades' outcome was decided by the engine's stop-wins-ties precedence rule rather than real intracandle price path data — treat results with more skepticism the higher this ratio is."}, 'winRate': {'type': 'number', 'description': 'wins / trades. Range 0–1.'}, 'avgLossR': {'type': 'number', 'description': 'Average R on losing trades (-1.0 with the default riskManagement; varies if the strategy overrides riskManagement.stopLoss).'}, 'direction': {'type': 'string', 'description': '"long" or "short" — the direction the whole backtest traded.'}, 'totalFeeR': {'type': 'number', 'description': "Total R subtracted across all trades by configuration.entryFeePct/exitFeePct (0 if neither was set on the request). expectancyR/pnlR above are already net of this — TotalFeeR is just how much fees cost, for diagnostics. Caveat: a trade's fee-in-R cost scales inversely with that trade's own stop distance, so once fees are applied, expectancyR is only a fair comparison WITHIN one archetype's own stop convention — use pnlPct-based metrics (see get_backtest_trades) for comparisons across strategies/timeframes with different typical stop widths."}, 'grossLossR': {'type': 'number', 'description': 'Sum of all losing trades, in R-units (negative).'}, 'expectancyR': {'type': 'number', 'description': 'Average expected R per trade — (winRate * avgWinR) + (lossRate * avgLossR). This is the metric to optimize a strategy on, not raw winRate or trade count: a low win rate with a high avgWinR/avgLossR ratio can still have a strongly positive expectancyR.'}, 'grossProfitR': {'type': 'number', 'description': 'Sum of all winning trades, in R-units.'}, 'maxDrawdownR': {'type': 'number', 'description': 'Worst peak-to-trough dip across closed trades, in R-units. 0 if equity never fell below its running high-water mark. Historical/all-time — see CurrentDrawdownR for where the equity curve sits right now.'}, 'profitFactor': {'type': ['number', 'null'], 'description': 'grossProfitR / abs(grossLossR). Greater than 1 means profitable. Null when there are no losing trades — the ratio is undefined (division by zero), not infinite.'}, 'currentDrawdownR': {'type': 'number', 'description': "How far below its own peak the equity curve sits at the end of the backtest window, in R-units. 0 if the backtest ends at a new high. Includes any still-open position's unrealized PnL (see UnrealizedPnlRAtEnd) — a slump caused by an open, underwater position at window end shows up here."}, 'openPositionsAtEnd': {'type': 'integer', 'description': "Number of positions still open (never hit stop/take/exit-signal) when the backtest's date range ended. 0 in the common case. Check this before trusting CurrentDrawdownR/UnrealizedPnlRAtEnd at face value."}, 'unrealizedPnlRAtEnd': {'type': 'number', 'description': "Sum of unrealized PnL, in R-units, across all positions still open at window end — marked to market against the last available candle's close. 0 when OpenPositionsAtEnd is 0. Does NOT include exit fee (the position hasn't closed, so none has been paid) — a slight overestimate of true current drawdown when a position is open. This value feeds ONLY CurrentDrawdownR/MaxDrawdownR — it is never included in PnlR/ExpectancyR/Trades/Wins/Losses or any other metric describing closed, realized trades."}, 'scoreDistributionPct': {'type': 'object', 'description': 'For each possible total score value: fraction of candles that summed to it.', 'additionalProperties': {'type': 'number'}}, 'confirmedSignalsCount': {'type': 'integer', 'description': 'Only meaningful when the request declared confirmationSources — how many candidates cleared confirmation and became one of the Trades above.'}, 'unconfirmedSignalsCount': {'type': 'integer', 'description': "Only meaningful when the request declared confirmationSources — how many candidates were dropped before trade simulation because they weren't corroborated. ConfirmedSignalsCount + UnconfirmedSignalsCount is the total candidate count, same as what a backtest without confirmationSources would have produced."}, 'conditionsDistributionPct': {'type': 'object', 'description': 'For each possible count of simultaneously-true conditions (0, 1, 2...N): fraction of all candles where exactly that many were true at once. A strategy-tuning diagnostic (how selective is the entry setup), not a performance metric.', 'additionalProperties': {'type': 'number'}}}}, 'status': {'type': 'string'}, 'runtimeMs': {'type': ['integer', 'null'], 'description': 'How long the analyser actually took to run this backtest, in milliseconds — pure compute time, not counting queue/messaging latency. Null until Status is "Completed".'}, 'errorMessage': {'type': ['string', 'null']}, 'recentAvgPnlR': {'type': ['number', 'null'], 'description': 'Average pnlR of the most recent RecentTradeCount closed trades. This is the recency signal for a rolling ranking — weighted alongside expectancyR, not a replacement for it.'}, 'unitsConsumed': {'type': ['number', 'null'], 'description': 'The actual execution-unit cost of this backtest, matching what\'s debited from the account\'s plan balance (CandlesProcessed normalized by the account\'s candles-per-unit rate). Null until Status is "Completed".'}, 'recentOutcomes': {'type': ['array', 'null'], 'items': {'type': 'string'}, 'description': '"Win"/"Loss" per recent trade, chronological — oldest first, so the LAST element is the most recent trade. Lets a caller see whether recent trades were genuinely a streak (e.g. all "Loss") versus alternating, which RecentAvgPnlR alone can\'t distinguish.'}, 'candlesProcessed': {'type': ['integer', 'null'], 'description': 'Number of candles the analyser processed for this backtest. Null until Status is "Completed". This is raw volume, not the plan\'s billing unit — see UnitsConsumed for that.'}, 'recentTradeCount': {'type': ['integer', 'null'], 'description': 'Number of trades the recency fields below are based on (up to 5, fewer if the backtest has fewer trades). Null until Status is "Completed".'}}}
输入模式
{'type': 'object', 'properties': {'id': {'type': ['string', 'null'], 'default': None, 'description': 'The id returned by submit_backtest.'}, 'page': {'type': 'integer', 'default': 1, 'description': '1-based page number. Default 1.'}, 'sortBy': {'type': ['string', 'null'], 'default': None, 'description': 'One of: number, pnlR, pnlPct, entryTime, exitTime. Defaults to number (closing order).'}, 'pageSize': {'type': 'integer', 'default': 20, 'description': 'Trades per page, 1-100. Default 20.'}, 'sortDirection': {'type': ['string', 'null'], 'default': None, 'description': '"asc" or "desc". Defaults to asc.'}, 'backtestApiKey': {'type': ['string', 'null'], 'default': None, 'description': "Your EmidLabs backtest API key. Not needed if this connector was added with a static 'x-api-key' header."}, 'backtestBaseUrl': {'type': ['string', 'null'], 'default': None, 'description': 'Defaults to the public production API.'}}}
输出模式
{'type': 'object', 'properties': {'page': {'type': 'integer'}, 'items': {'type': 'array', 'items': {'type': 'object', 'properties': {'feeR': {'type': 'number', 'description': "R-units subtracted from this trade's PnlR by configuration.entryFeePct/exitFeePct (0 if neither was set). PnlR is already net of this — PnlR + FeeR recovers the pre-fee raw R. Not directly comparable across trades with different RiskDistance: this scales inversely with each trade's own stop distance."}, 'pnlR': {'type': 'number', 'description': "This trade's PnL in R-units (risk multiples)."}, 'number': {'type': 'integer', 'description': '1-based order this trade closed in across the whole backtest (not just this page).'}, 'pnlPct': {'type': 'number', 'description': "This trade's PnL as a percent price move."}, 'exitPrice': {'type': 'number', 'description': "Fill price at exit — StopPrice, TakePrice, or the exit candle's close, depending on ExitReason."}, 'stopPrice': {'type': 'number', 'description': 'The stop-loss price this trade was risk-managed against.'}, 'takePrice': {'type': 'number', 'description': 'The take-profit price this trade was risk-managed against.'}, 'entryPrice': {'type': 'number', 'description': "Fill price at entry — always the entry candle's close."}, 'exitReason': {'type': 'string', 'description': '"StopLoss", "TakeProfit", or "Signal" (closed via decision.exit).'}, 'riskDistance': {'type': 'number', 'description': 'Price distance between EntryPrice and StopPrice — the denominator PnlR/FeeR are expressed in units of.'}, 'holdingCandles': {'type': 'integer', 'description': 'Timeframe-agnostic candle count the position was held for (exit candle index minus entry candle index). Minimum possible value is 1, not 0 — a position opened on candle i can earliest close on candle i+1. Derive real elapsed time from EntryExecutionCandleOpenTime/ExitExecutionCandleOpenTime if needed.'}, 'conditionsAtEntry': {'type': ['object', 'null'], 'description': 'Every named condition from strategySnapshotJson.conditions at entry time — true AND false, not just the ones that were true.', 'additionalProperties': {'type': 'boolean'}}, 'totalScoreAtEntry': {'type': ['integer', 'null'], 'description': 'Total score at entry — the value compared against the threshold in decision.entry.'}, 'scoreBreakdownAtEntry': {'type': ['object', 'null'], 'description': 'Weight contributed by each condition toward TotalScoreAtEntry — 0 for conditions that were false.', 'additionalProperties': {'type': 'integer'}}, 'exitSignalCandleOpenTime': {'type': 'integer', 'description': "Candle open time this trade's exit signal fired on. Currently always equal to ExitExecutionCandleOpenTime — same reserved-for-future-use note as the entry pair."}, 'entrySignalCandleOpenTime': {'type': 'integer', 'description': "Candle open time this trade's entry signal fired on. Currently always equal to EntryExecutionCandleOpenTime — reserved for a future delayed-fill model (e.g. signal on candle close, execution on next candle open), not yet implemented. Don't rely on these differing today."}, 'exitExecutionCandleOpenTime': {'type': 'integer', 'description': "Candle open time this trade's exit actually filled on. Currently always equal to ExitSignalCandleOpenTime — see that field's note."}, 'entryExecutionCandleOpenTime': {'type': 'integer', 'description': "Candle open time this trade's entry actually filled on. Currently always equal to EntrySignalCandleOpenTime — see that field's note."}}}, 'description': 'The trades on this page, in the requested sort order.'}, 'pageSize': {'type': 'integer'}, 'totalCount': {'type': 'integer', 'description': 'Total closed trades across the whole backtest, not just this page.'}, 'totalPages': {'type': 'integer'}}}
输入模式
{'type': 'object', 'properties': {'id': {'type': ['string', 'null'], 'default': None, 'description': 'The id returned by submit_confirmation_source.'}, 'pollTimeoutMs': {'type': 'integer', 'default': 120000, 'description': 'Defaults to 120000 (2 minutes).'}, 'backtestApiKey': {'type': ['string', 'null'], 'default': None, 'description': "Your EmidLabs backtest API key. Not needed if this connector was added with a static 'x-api-key' header."}, 'backtestBaseUrl': {'type': ['string', 'null'], 'default': None, 'description': 'Defaults to the public production API.'}, 'waitForCompletion': {'type': 'boolean', 'default': True, 'description': 'If true (default), polls internally until the confirmation source finishes or pollTimeoutMs elapses.'}}}
输出模式
{'type': 'object', 'properties': {'id': {'type': 'string'}, 'status': {'type': 'string'}, 'assetPair': {'type': 'string'}, 'runtimeMs': {'type': ['integer', 'null'], 'description': 'How long the analyser actually took to compute this confirmation source\'s signal timeline, in milliseconds. Null until Status is "Completed".'}, 'timeframe': {'type': 'string'}, 'errorMessage': {'type': ['string', 'null']}, 'unitsConsumed': {'type': ['number', 'null'], 'description': "The actual execution-unit cost, same pool/rate as a regular backtest — computing a confirmation source's signal timeline costs the same per candle as simulating trades, it just skips the trade simulation itself."}, 'candlesProcessed': {'type': ['integer', 'null'], 'description': 'Number of candles the analyser processed. Null until Status is "Completed". This is raw volume, not the plan\'s billing unit — see UnitsConsumed for that.'}}}
输入模式
{'type': 'object', 'properties': {'id': {'type': ['string', 'null'], 'default': None, 'description': 'The id returned by submit_confirmation_source.'}, 'page': {'type': 'integer', 'default': 1, 'description': '1-based page number. Default 1.'}, 'pageSize': {'type': 'integer', 'default': 20, 'description': 'Signals per page, 1-100. Default 20.'}, 'backtestApiKey': {'type': ['string', 'null'], 'default': None, 'description': "Your EmidLabs backtest API key. Not needed if this connector was added with a static 'x-api-key' header."}, 'backtestBaseUrl': {'type': ['string', 'null'], 'default': None, 'description': 'Defaults to the public production API.'}}}
输出模式
{'type': 'object', 'properties': {'page': {'type': 'integer'}, 'items': {'type': 'array', 'items': {'type': 'object', 'properties': {'type': {'type': 'string', 'description': '"Entry" or "Exit".'}, 'score': {'type': 'integer', 'description': 'Total score at signal time.'}, 'conditions': {'type': 'object', 'description': 'Every named condition from strategySnapshotJson.conditions at signal time — true AND false, not just the ones that were true.', 'additionalProperties': {'type': 'boolean'}}, 'candleOpenTime': {'type': 'integer', 'description': 'Open time (unix epoch) of the candle whose close triggered this signal.'}, 'scoreBreakdown': {'type': 'object', 'description': 'Weight contributed by each condition toward Score.', 'additionalProperties': {'type': 'integer'}}}}, 'description': 'The signals on this page, in candle order.'}, 'pageSize': {'type': 'integer'}, 'totalCount': {'type': 'integer', 'description': 'Total signals across the whole confirmation source, not just this page — can easily be in the thousands for a frequent condition over a long range.'}, 'totalPages': {'type': 'integer'}}}
输入模式
{'type': 'object', 'properties': {'backtestApiKey': {'type': ['string', 'null'], 'default': None, 'description': "Your EmidLabs backtest API key. Not needed if this connector was added with a static 'x-api-key' header."}, 'backtestBaseUrl': {'type': ['string', 'null'], 'default': None, 'description': 'Defaults to the public production API. Override only for self-hosted/staging use.'}}}
输出模式
{'type': 'object', 'properties': {'assets': {'type': 'array', 'items': {'type': 'object', 'required': ['assetPair'], 'properties': {'assetPair': {'type': 'string'}, 'timeframes': {'type': 'array', 'items': {'type': 'string'}}, 'lastAvailable': {'type': 'string', 'format': 'date-time'}, 'firstAvailable': {'type': 'string', 'format': 'date-time'}}}}}}
输入模式
{'type': 'object', 'properties': {'assetPair': {'type': ['string', 'null'], 'default': None, 'description': 'e.g. "BTC-USDC".'}, 'finalDate': {'type': ['string', 'null'], 'default': None, 'description': 'ISO date string, e.g. "2025-06-01".'}, 'initialDate': {'type': ['string', 'null'], 'default': None, 'description': 'ISO date string, e.g. "2025-01-01".'}, 'backtestApiKey': {'type': ['string', 'null'], 'default': None, 'description': "Your EmidLabs backtest API key (created in the Console). Not needed if this connector was added with a static 'x-api-key' header."}, 'backtestBaseUrl': {'type': ['string', 'null'], 'default': None, 'description': 'Defaults to the public production API. Override only for self-hosted/staging use.'}, 'confirmationSources': {'type': ['array', 'null'], 'items': {'type': ['object', 'null'], 'required': ['sourceId'], 'properties': {'sourceId': {'type': ['string', 'null'], 'description': "The id returned by submit_confirmation_source (same account only) whose signal timeline this backtest's candidates must be corroborated by."}, 'signalType': {'type': ['string', 'null'], 'description': '"entry" or "exit" — which of the source\'s signal types counts as confirming. Defaults to "entry" when omitted.'}, 'validityWindow': {'type': ['object', 'null'], 'properties': {'count': {'type': 'integer', 'description': "Number of candles of the source's own timeframe. Defaults to 1 when omitted."}}, 'description': 'An OBJECT, not a bare number — e.g. { "count": 5 }. Defaults to { "count": 1 } when omitted.'}}, 'description': "One confirmation requirement: this backtest's own candidate Entry/Exit only counts as a trade once sourceId's own signal timeline is corroborated within validityWindow."}, 'default': None, 'description': "Optional. Each entry names a submit_confirmation_source result (same account only, must already be Completed) that every candidate Entry/Exit must be corroborated by before it's simulated as a trade — an unconfirmed candidate is dropped before trade simulation, never appears in get_backtest_trades or affects PnlR/WinRate/etc. See ConfirmedSignalsCount/UnconfirmedSignalsCount on get_backtest_result. A sourceId that doesn't exist, isn't Completed, or belongs to another account fails this submission immediately (unlike live, this is synchronous/batch — letting it through would produce a confusing zero-trade result with no explanation)."}, 'strategySnapshotJson': {'type': ['object', 'null'], 'default': None, 'properties': {'score': {'type': ['object', 'null'], 'description': 'Integer weight per condition name (keys must match "conditions"). The engine sums the weights of every true condition on a candle; compare the total against a threshold in "decision.entry" (e.g. "score >= 50").', 'additionalProperties': {'type': 'integer'}}, 'inputs': {'type': ['object', 'null'], 'description': 'Named computed values, one JSON-string expression each, evaluated once per candle. Raw fields: close, open, high, low, volume. Built-in functions (case-sensitive, lowercase — no others exist, e.g. avg()/SMA() uppercase/close[N] bracket indexing are NOT supported): ema(series,period), sma(series,period), rsi(series,period), atr(period), adx(period), adxPlusDi(period), adxMinusDi(period), crossUp(a,b), crossDown(a,b), highest(series,n), lowest(series,n), change(series), shift(series,n) [look-back only, NaN before enough history], any(boolSeries,n), all(boolSeries,n), count(boolSeries,n) [over the n candles before the current one], swingHigh(series,confirmBars), swingLow(series,confirmBars) [confirmed N-bar swing point, true confirmBars candles AFTER the actual peak/trough — never at the peak itself, so it can\'t repaint between backtest and live], body(), range(), upperWick(), lowerWick(), isBullish(), isBearish(), abs(x), min(a,b), max(a,b). There is no volumeSma()/volumeSpike() — volume is a plain series like close/open/high/low, so use sma(volume,period) and volume > sma(volume,period) * multiplier instead. Candlestick patterns (all no-arg, boolean, read only closed-candle OHLC, same in backtest and live): single-candle hammer(), shootingStar(), doji(), bullishMarubozu(), bearishMarubozu(), spinningTop(), dragonflyDoji(), gravestoneDoji(), longLeggedDoji(); two-candle bullishEngulfing(), bearishEngulfing(), piercingLine(), darkCloudCover(), bullishHarami(), bearishHarami(), haramiCross(), tweezerTop(), tweezerBottom(); three-plus-candle morningStar(), eveningStar(), threeWhiteSoldiers(), threeBlackCrows(), threeInsideUp(), threeInsideDown(), threeOutsideUp(), threeOutsideDown(), risingThreeMethods(), fallingThreeMethods(). Caveat: hammer()/shootingStar() are shape-only, no prior-trend check (same shape is Hammer in a downtrend but Hanging Man in an uptrend, and vice versa for shootingStar/Inverted Hammer) — pair with a trend/momentum condition rather than using the shape alone. An input may only reference inputs defined above it (no forward/circular references). Example: {"emaFast":"ema(close, 9)","emaSlow":"ema(close, 21)"}', 'additionalProperties': {'type': ['string', 'null']}}, 'decision': {'type': ['object', 'null'], 'properties': {'exit': {'type': ['string', 'null'], 'description': 'Optional boolean expression; closes an open position when true, independent of riskManagement. If a candle\'s stop-loss/take-profit and "exit" would both trigger, the stop-loss/take-profit wins and exit is not evaluated for that position that candle. If omitted, positions only close via stop-loss/take-profit.'}, 'entry': {'type': ['string', 'null'], 'description': 'Boolean expression evaluated per candle; a trade opens when true.'}}}, 'conditions': {'type': ['object', 'null'], 'description': 'Named boolean expressions over inputs/market data. Operators: > < >= <= == != AND OR. Example: {"trendUp":"emaFast > emaSlow AND adx14 > 20"}', 'additionalProperties': {'type': ['string', 'null']}}, 'configuration': {'type': ['object', 'null'], 'properties': {'timezone': {'type': ['string', 'null'], 'description': 'IANA timezone id (e.g. "America/Sao_Paulo") used to localize the DSL\'s hour()/minute()/dayOfWeek()/isWeekend() functions, for time-of-day or weekday gating conditions. Omit/null defaults to "America/Sao_Paulo".'}, 'direction': {'type': ['string', 'null'], 'description': '"long" or "short" — picks which side the WHOLE backtest trades, not a mix. Only affects stop-loss/take-profit price placement and the profit/loss sign (mirrored for short: stop above entry, take below entry, profit when price falls) — conditions/score/decision keep the exact same meaning either way. Defaults to "long".'}, 'timeframe': {'type': ['string', 'null'], 'description': 'One of "5M", "15M", "30M", "1H", "2H", "4H", "1D".'}, 'exitFeePct': {'type': ['number', 'null'], 'description': 'Simulated exchange fee on exit, as a percent (e.g. 0.1 = 0.1%). Same semantics as EntryFeePct — real exchanges can charge different maker/taker rates per leg, so this is independent, not assumed equal.'}, 'warmupBars': {'type': 'integer', 'description': 'Leading candles excluded from trading while indicators stabilize. Default 0.'}, 'entryFeePct': {'type': ['number', 'null'], 'description': "Simulated exchange fee on entry, as a percent (e.g. 0.1 = 0.1%). Omit/null = 0 (no fee, backward compatible). Subtracted from every trade's pnlR/pnlPct so results are net-of-fee by construction. 0-5 range. See the pnlR/expectancyR field descriptions on get_backtest_result for the R-vs-% caveat once a fee is set."}, 'maxOpenPositions': {'type': ['integer', 'null'], 'description': 'Caps concurrent open trades. Omit/null for unlimited (default).'}}}, 'riskManagement': {'type': ['object', 'null'], 'properties': {'stopLoss': {'description': 'Stop-loss policy. Exactly one of: {"type":"percent","percent":<number, e.g. 1.0 for 1%>} or {"type":"atr","period":<int>,"multiplier":<number>}. "type" is required; field order does not matter.'}, 'takeProfit': {'description': 'Take-profit policy. Exactly one of: {"type":"riskReward","multiple":<number>} (distance = stop-loss distance * multiple) or {"type":"percent","percent":<number>}. "type" is required; field order does not matter.'}}, 'description': 'Configures stop-loss/take-profit. Omit entirely for the default (1% stop-loss, 1:3 risk-reward take-profit) — see stopLoss/takeProfit below for the full shape of each.'}}, 'description': 'The Strategy DSL object — every field below documents its own exact shape, this is just the execution model that ties them together. Entry fills at the close of the candle where decision.entry turns true (no lookahead). A position closes on the first of these to happen, checked in this order: stop-loss hit, take-profit hit, decision.exit turning true (a same-candle stop/take-profit always wins over exit). Multiple positions can be open at once by default — cap with configuration.maxOpenPositions. Results are measured in R-units (risk multiples); expectancyR (average R per trade) is the metric to optimize, not raw win rate or trade count.'}}}
输出模式
{'type': 'object', 'properties': {'id': {'type': 'string'}, 'status': {'type': 'string'}}}
输入模式
{'type': 'object', 'properties': {'finalDate': {'type': ['string', 'null'], 'default': None, 'description': 'ISO date string, e.g. "2025-06-01".'}, 'assetPairs': {'type': ['array', 'null'], 'items': {'type': ['string', 'null']}, 'default': None, 'description': 'e.g. ["BTC-USDC", "ETH-USDC", "SOL-USDC"]. Every asset gets the exact same strategySnapshotJson and date range.'}, 'initialDate': {'type': ['string', 'null'], 'default': None, 'description': 'ISO date string, e.g. "2025-01-01".'}, 'backtestApiKey': {'type': ['string', 'null'], 'default': None, 'description': "Your EmidLabs backtest API key (created in the Console). Not needed if this connector was added with a static 'x-api-key' header."}, 'backtestBaseUrl': {'type': ['string', 'null'], 'default': None, 'description': 'Defaults to the public production API. Override only for self-hosted/staging use.'}, 'confirmationSources': {'type': ['string', 'null'], 'default': None, 'description': 'Optional. A JSON-encoded STRING (not a native array/object — pass it exactly like a quoted string value), containing the same shape as submit_backtest\'s own confirmationSources: [{"sourceId":"<a submit_confirmation_source id>","signalType":"entry","validityWindow":{"count":1}}]. Applied identically to every asset pair in this batch (one shared gate, not one per asset). Passed as a raw JSON string rather than a native array because this tool already has one array parameter (assetPairs) — a second one crashes the MCP SDK\'s own parameter marshaller. A sourceId that doesn\'t exist, isn\'t Completed, or belongs to another account fails that item only (same best-effort semantics as an unknown asset pair), not the whole batch.'}, 'strategySnapshotJson': {'type': ['object', 'null'], 'default': None, 'properties': {'score': {'type': ['object', 'null'], 'description': 'Integer weight per condition name (keys must match "conditions"). The engine sums the weights of every true condition on a candle; compare the total against a threshold in "decision.entry" (e.g. "score >= 50").', 'additionalProperties': {'type': 'integer'}}, 'inputs': {'type': ['object', 'null'], 'description': 'Named computed values, one JSON-string expression each, evaluated once per candle. Raw fields: close, open, high, low, volume. Built-in functions (case-sensitive, lowercase — no others exist, e.g. avg()/SMA() uppercase/close[N] bracket indexing are NOT supported): ema(series,period), sma(series,period), rsi(series,period), atr(period), adx(period), adxPlusDi(period), adxMinusDi(period), crossUp(a,b), crossDown(a,b), highest(series,n), lowest(series,n), change(series), shift(series,n) [look-back only, NaN before enough history], any(boolSeries,n), all(boolSeries,n), count(boolSeries,n) [over the n candles before the current one], swingHigh(series,confirmBars), swingLow(series,confirmBars) [confirmed N-bar swing point, true confirmBars candles AFTER the actual peak/trough — never at the peak itself, so it can\'t repaint between backtest and live], body(), range(), upperWick(), lowerWick(), isBullish(), isBearish(), abs(x), min(a,b), max(a,b). There is no volumeSma()/volumeSpike() — volume is a plain series like close/open/high/low, so use sma(volume,period) and volume > sma(volume,period) * multiplier instead. Candlestick patterns (all no-arg, boolean, read only closed-candle OHLC, same in backtest and live): single-candle hammer(), shootingStar(), doji(), bullishMarubozu(), bearishMarubozu(), spinningTop(), dragonflyDoji(), gravestoneDoji(), longLeggedDoji(); two-candle bullishEngulfing(), bearishEngulfing(), piercingLine(), darkCloudCover(), bullishHarami(), bearishHarami(), haramiCross(), tweezerTop(), tweezerBottom(); three-plus-candle morningStar(), eveningStar(), threeWhiteSoldiers(), threeBlackCrows(), threeInsideUp(), threeInsideDown(), threeOutsideUp(), threeOutsideDown(), risingThreeMethods(), fallingThreeMethods(). Caveat: hammer()/shootingStar() are shape-only, no prior-trend check (same shape is Hammer in a downtrend but Hanging Man in an uptrend, and vice versa for shootingStar/Inverted Hammer) — pair with a trend/momentum condition rather than using the shape alone. An input may only reference inputs defined above it (no forward/circular references). Example: {"emaFast":"ema(close, 9)","emaSlow":"ema(close, 21)"}', 'additionalProperties': {'type': ['string', 'null']}}, 'decision': {'type': ['object', 'null'], 'properties': {'exit': {'type': ['string', 'null'], 'description': 'Optional boolean expression; closes an open position when true, independent of riskManagement. If a candle\'s stop-loss/take-profit and "exit" would both trigger, the stop-loss/take-profit wins and exit is not evaluated for that position that candle. If omitted, positions only close via stop-loss/take-profit.'}, 'entry': {'type': ['string', 'null'], 'description': 'Boolean expression evaluated per candle; a trade opens when true.'}}}, 'conditions': {'type': ['object', 'null'], 'description': 'Named boolean expressions over inputs/market data. Operators: > < >= <= == != AND OR. Example: {"trendUp":"emaFast > emaSlow AND adx14 > 20"}', 'additionalProperties': {'type': ['string', 'null']}}, 'configuration': {'type': ['object', 'null'], 'properties': {'timezone': {'type': ['string', 'null'], 'description': 'IANA timezone id (e.g. "America/Sao_Paulo") used to localize the DSL\'s hour()/minute()/dayOfWeek()/isWeekend() functions, for time-of-day or weekday gating conditions. Omit/null defaults to "America/Sao_Paulo".'}, 'direction': {'type': ['string', 'null'], 'description': '"long" or "short" — picks which side the WHOLE backtest trades, not a mix. Only affects stop-loss/take-profit price placement and the profit/loss sign (mirrored for short: stop above entry, take below entry, profit when price falls) — conditions/score/decision keep the exact same meaning either way. Defaults to "long".'}, 'timeframe': {'type': ['string', 'null'], 'description': 'One of "5M", "15M", "30M", "1H", "2H", "4H", "1D".'}, 'exitFeePct': {'type': ['number', 'null'], 'description': 'Simulated exchange fee on exit, as a percent (e.g. 0.1 = 0.1%). Same semantics as EntryFeePct — real exchanges can charge different maker/taker rates per leg, so this is independent, not assumed equal.'}, 'warmupBars': {'type': 'integer', 'description': 'Leading candles excluded from trading while indicators stabilize. Default 0.'}, 'entryFeePct': {'type': ['number', 'null'], 'description': "Simulated exchange fee on entry, as a percent (e.g. 0.1 = 0.1%). Omit/null = 0 (no fee, backward compatible). Subtracted from every trade's pnlR/pnlPct so results are net-of-fee by construction. 0-5 range. See the pnlR/expectancyR field descriptions on get_backtest_result for the R-vs-% caveat once a fee is set."}, 'maxOpenPositions': {'type': ['integer', 'null'], 'description': 'Caps concurrent open trades. Omit/null for unlimited (default).'}}}, 'riskManagement': {'type': ['object', 'null'], 'properties': {'stopLoss': {'description': 'Stop-loss policy. Exactly one of: {"type":"percent","percent":<number, e.g. 1.0 for 1%>} or {"type":"atr","period":<int>,"multiplier":<number>}. "type" is required; field order does not matter.'}, 'takeProfit': {'description': 'Take-profit policy. Exactly one of: {"type":"riskReward","multiple":<number>} (distance = stop-loss distance * multiple) or {"type":"percent","percent":<number>}. "type" is required; field order does not matter.'}}, 'description': 'Configures stop-loss/take-profit. Omit entirely for the default (1% stop-loss, 1:3 risk-reward take-profit) — see stopLoss/takeProfit below for the full shape of each.'}}, 'description': "Same Strategy DSL object submit_backtest takes — see that tool's description for the full shape."}}}
输出模式
{'type': 'object', 'properties': {'items': {'type': 'array', 'items': {'type': 'object', 'properties': {'id': {'type': ['string', 'null'], 'description': 'Null if this asset pair failed at submission (see Error) — never appears in get_backtest_batch_results either, since no backtest record was ever created for it.'}, 'error': {'type': ['string', 'null'], 'description': 'Set only if this asset pair failed at submission (e.g. unknown asset pair, date range outside coverage). The other items in the batch are unaffected.'}, 'status': {'type': ['string', 'null']}, 'assetPair': {'type': 'string'}}}}, 'batchId': {'type': 'string', 'description': "Pass this to get_backtest_batch_results to fetch every item's status/result, paginated."}}}
输入模式
{'type': 'object', 'properties': {'assetPair': {'type': ['string', 'null'], 'default': None, 'description': 'e.g. "BTC-USDC".'}, 'finalDate': {'type': ['string', 'null'], 'default': None, 'description': 'ISO date string, e.g. "2025-06-01".'}, 'initialDate': {'type': ['string', 'null'], 'default': None, 'description': 'ISO date string, e.g. "2025-01-01".'}, 'backtestApiKey': {'type': ['string', 'null'], 'default': None, 'description': "Your EmidLabs backtest API key (created in the Console). Not needed if this connector was added with a static 'x-api-key' header."}, 'backtestBaseUrl': {'type': ['string', 'null'], 'default': None, 'description': 'Defaults to the public production API. Override only for self-hosted/staging use.'}, 'strategySnapshotJson': {'type': ['object', 'null'], 'default': None, 'properties': {'score': {'type': ['object', 'null'], 'description': 'Integer weight per condition name (keys must match "conditions"). The engine sums the weights of every true condition on a candle; compare the total against a threshold in "decision.entry" (e.g. "score >= 50").', 'additionalProperties': {'type': 'integer'}}, 'inputs': {'type': ['object', 'null'], 'description': 'Named computed values, one JSON-string expression each, evaluated once per candle. Raw fields: close, open, high, low, volume. Built-in functions (case-sensitive, lowercase — no others exist, e.g. avg()/SMA() uppercase/close[N] bracket indexing are NOT supported): ema(series,period), sma(series,period), rsi(series,period), atr(period), adx(period), adxPlusDi(period), adxMinusDi(period), crossUp(a,b), crossDown(a,b), highest(series,n), lowest(series,n), change(series), shift(series,n) [look-back only, NaN before enough history], any(boolSeries,n), all(boolSeries,n), count(boolSeries,n) [over the n candles before the current one], swingHigh(series,confirmBars), swingLow(series,confirmBars) [confirmed N-bar swing point, true confirmBars candles AFTER the actual peak/trough — never at the peak itself, so it can\'t repaint between backtest and live], body(), range(), upperWick(), lowerWick(), isBullish(), isBearish(), abs(x), min(a,b), max(a,b). There is no volumeSma()/volumeSpike() — volume is a plain series like close/open/high/low, so use sma(volume,period) and volume > sma(volume,period) * multiplier instead. Candlestick patterns (all no-arg, boolean, read only closed-candle OHLC, same in backtest and live): single-candle hammer(), shootingStar(), doji(), bullishMarubozu(), bearishMarubozu(), spinningTop(), dragonflyDoji(), gravestoneDoji(), longLeggedDoji(); two-candle bullishEngulfing(), bearishEngulfing(), piercingLine(), darkCloudCover(), bullishHarami(), bearishHarami(), haramiCross(), tweezerTop(), tweezerBottom(); three-plus-candle morningStar(), eveningStar(), threeWhiteSoldiers(), threeBlackCrows(), threeInsideUp(), threeInsideDown(), threeOutsideUp(), threeOutsideDown(), risingThreeMethods(), fallingThreeMethods(). Caveat: hammer()/shootingStar() are shape-only, no prior-trend check (same shape is Hammer in a downtrend but Hanging Man in an uptrend, and vice versa for shootingStar/Inverted Hammer) — pair with a trend/momentum condition rather than using the shape alone. An input may only reference inputs defined above it (no forward/circular references). Example: {"emaFast":"ema(close, 9)","emaSlow":"ema(close, 21)"}', 'additionalProperties': {'type': ['string', 'null']}}, 'decision': {'type': ['object', 'null'], 'properties': {'exit': {'type': ['string', 'null'], 'description': 'Optional boolean expression; closes an open position when true, independent of riskManagement. If a candle\'s stop-loss/take-profit and "exit" would both trigger, the stop-loss/take-profit wins and exit is not evaluated for that position that candle. If omitted, positions only close via stop-loss/take-profit.'}, 'entry': {'type': ['string', 'null'], 'description': 'Boolean expression evaluated per candle; a trade opens when true.'}}}, 'conditions': {'type': ['object', 'null'], 'description': 'Named boolean expressions over inputs/market data. Operators: > < >= <= == != AND OR. Example: {"trendUp":"emaFast > emaSlow AND adx14 > 20"}', 'additionalProperties': {'type': ['string', 'null']}}, 'configuration': {'type': ['object', 'null'], 'properties': {'timezone': {'type': ['string', 'null'], 'description': 'IANA timezone id (e.g. "America/Sao_Paulo") used to localize the DSL\'s hour()/minute()/dayOfWeek()/isWeekend() functions, for time-of-day or weekday gating conditions. Omit/null defaults to "America/Sao_Paulo".'}, 'direction': {'type': ['string', 'null'], 'description': '"long" or "short" — picks which side the WHOLE backtest trades, not a mix. Only affects stop-loss/take-profit price placement and the profit/loss sign (mirrored for short: stop above entry, take below entry, profit when price falls) — conditions/score/decision keep the exact same meaning either way. Defaults to "long".'}, 'timeframe': {'type': ['string', 'null'], 'description': 'One of "5M", "15M", "30M", "1H", "2H", "4H", "1D".'}, 'exitFeePct': {'type': ['number', 'null'], 'description': 'Simulated exchange fee on exit, as a percent (e.g. 0.1 = 0.1%). Same semantics as EntryFeePct — real exchanges can charge different maker/taker rates per leg, so this is independent, not assumed equal.'}, 'warmupBars': {'type': 'integer', 'description': 'Leading candles excluded from trading while indicators stabilize. Default 0.'}, 'entryFeePct': {'type': ['number', 'null'], 'description': "Simulated exchange fee on entry, as a percent (e.g. 0.1 = 0.1%). Omit/null = 0 (no fee, backward compatible). Subtracted from every trade's pnlR/pnlPct so results are net-of-fee by construction. 0-5 range. See the pnlR/expectancyR field descriptions on get_backtest_result for the R-vs-% caveat once a fee is set."}, 'maxOpenPositions': {'type': ['integer', 'null'], 'description': 'Caps concurrent open trades. Omit/null for unlimited (default).'}}}, 'riskManagement': {'type': ['object', 'null'], 'properties': {'stopLoss': {'description': 'Stop-loss policy. Exactly one of: {"type":"percent","percent":<number, e.g. 1.0 for 1%>} or {"type":"atr","period":<int>,"multiplier":<number>}. "type" is required; field order does not matter.'}, 'takeProfit': {'description': 'Take-profit policy. Exactly one of: {"type":"riskReward","multiple":<number>} (distance = stop-loss distance * multiple) or {"type":"percent","percent":<number>}. "type" is required; field order does not matter.'}}, 'description': 'Configures stop-loss/take-profit. Omit entirely for the default (1% stop-loss, 1:3 risk-reward take-profit) — see stopLoss/takeProfit below for the full shape of each.'}}, 'description': "The Strategy DSL object — identical shape to submit_backtest's own. riskManagement is accepted but never used (no position is ever opened), so it's fine to omit."}}}
输出模式
{'type': 'object', 'properties': {'id': {'type': 'string'}, 'status': {'type': 'string'}}}
近期工具变更
类似的 MCP 服务器
DeltaSignal ATLAS-7
Analyzes SEC and XBRL data for crypto-related public companies, including fundamentals, issuer signals, peer comparisons, stress …
intel
Provides multi-chain market intelligence, blockchain activity metrics, trading and portfolio planning, sanctions clearance, agent…
Market Intelligence API
Analyzes crypto and tokenized-asset markets using DEX flows, perpetual positioning, smart-money activity, token risk, market news…
Gate MCP
Provides public Gate exchange market data for spot, futures, delivery, margin, options, Alpha, Earn products, order books, candle…
AgentFeed
Provides crypto market data on liquidations, funding, open interest, order books, spreads, liquidation forecasts, and tokenized-a…
whale-insights
Provides read-only crypto market and on-chain intelligence, including whale activity, analyst signals, prices, funding rates, gas…
Okx
Provides public OKX market data for crypto instruments, including candles, prices, order books, funding rates, open interest, pos…
mcp
Provides crypto derivatives and market-structure data including funding, open interest, liquidations, options, ETF flows, leverag…