This document describes the global validation parameters that control financial safety constraints in signal generation. These parameters enforce minimum take profit distances, maximum stop loss distances, and signal lifetime limits to protect capital from unprofitable trades, catastrophic losses, and strategy deadlock.
For information about other configuration parameters like timeout and timing constraints, see Timing Parameters. For an overview of the configuration system, see Global Configuration. For details on how signals are validated during generation, see Signal Generation and Validation.
The framework enforces four critical validation parameters that act as financial guardrails during signal generation. These parameters are part of GLOBAL_CONFIG and can be modified via setConfig() at runtime.
| Parameter | Type | Default | Purpose |
|---|---|---|---|
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT |
number | 0.5 | Minimum percentage distance between priceOpen and priceTakeProfit to ensure profit exceeds trading fees |
CC_MIN_STOPLOSS_DISTANCE_PERCENT |
number | 0.5 | Minimum percentage distance between priceOpen and priceStopLoss to prevent instant stop out on market volatility |
CC_MAX_STOPLOSS_DISTANCE_PERCENT |
number | 20 | Maximum percentage distance between priceOpen and priceStopLoss to prevent catastrophic losses |
CC_MAX_SIGNAL_LIFETIME_MINUTES |
number | 1440 | Maximum signal duration in minutes to prevent eternal signals blocking risk limits |
These validations occur in the VALIDATE_SIGNAL_FN function within ClientStrategy, which is called before every signal is created. Signals that fail validation are rejected immediately with descriptive error messages.
Trading fees (typically 0.1% per side) create a minimum profit threshold. If priceTakeProfit is too close to priceOpen, the gross profit will be consumed by fees, resulting in a net loss despite hitting the take profit target.
Example Scenario:
priceOpen = 42000, priceTakeProfit = 42010 (0.024% profit)Even hitting take profit results in a net loss because the gross profit fails to cover trading fees.
Diagram: Take Profit Distance Validation Flow
The validation calculates the percentage distance between entry price and take profit target, then compares it against CC_MIN_TAKEPROFIT_DISTANCE_PERCENT. For long positions, TP must be above entry. For short positions, TP must be below entry.
Long Position Validation (src/client/ClientStrategy.ts:138-148):
const tpDistancePercent = ((signal.priceTakeProfit - signal.priceOpen) / signal.priceOpen) * 100;
if (tpDistancePercent < GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT) {
errors.push(
`Long: TakeProfit too close to priceOpen (${tpDistancePercent.toFixed(3)}%). ` +
`Minimum distance: ${GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT}% to cover trading fees.`
);
}
Short Position Validation (src/client/ClientStrategy.ts:198-208):
const tpDistancePercent = ((signal.priceOpen - signal.priceTakeProfit) / signal.priceOpen) * 100;
if (tpDistancePercent < GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT) {
errors.push(
`Short: TakeProfit too close to priceOpen (${tpDistancePercent.toFixed(3)}%). ` +
`Minimum distance: ${GLOBAL_CONFIG.CC_MIN_TAKEPROFIT_DISTANCE_PERCENT}% to cover trading fees.`
);
}
The default value of 0.5% provides a safety margin above trading fees:
0 to disable validation (only for testing environments)Stop losses positioned too close to the entry price can result in instant stop-outs due to normal market volatility or spread fluctuations. This creates a scenario where positions are closed immediately after opening, even when the strategy's directional bias is correct.
Example Scenario:
priceOpen = 42000, priceStopLoss = 41990 (0.024% distance)Even with correct market direction, tight stop losses guarantee losses from noise.
Diagram: Minimum Stop Loss Distance Validation Flow
The validation calculates the percentage distance between entry price and stop loss, then compares it against CC_MIN_STOPLOSS_DISTANCE_PERCENT. For long positions, SL must be below entry with sufficient buffer. For short positions, SL must be above entry with sufficient buffer.
Long Position Validation (src/client/ClientStrategy.ts:176-186):
const slDistancePercent = ((signal.priceOpen - signal.priceStopLoss) / signal.priceOpen) * 100;
if (slDistancePercent < GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT) {
errors.push(
`Long: StopLoss too close to priceOpen (${slDistancePercent.toFixed(3)}%). ` +
`Minimum distance: ${GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT}% to avoid instant stop out on market volatility. ` +
`Current: SL=${signal.priceStopLoss}, Open=${signal.priceOpen}`
);
}
Short Position Validation (src/client/ClientStrategy.ts:267-277):
const slDistancePercent = ((signal.priceStopLoss - signal.priceOpen) / signal.priceOpen) * 100;
if (slDistancePercent < GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT) {
errors.push(
`Short: StopLoss too close to priceOpen (${slDistancePercent.toFixed(3)}%). ` +
`Minimum distance: ${GLOBAL_CONFIG.CC_MIN_STOPLOSS_DISTANCE_PERCENT}% to avoid instant stop out on market volatility. ` +
`Current: SL=${signal.priceStopLoss}, Open=${signal.priceOpen}`
);
}
The default value of 0.5% provides a buffer against normal market noise:
0.2-0.3% for stable forex pairs with tight spreads1-2% for volatile crypto markets0 in production (disables protection against micro-stops)Excessively wide stop losses can expose the portfolio to catastrophic single-trade losses. A stop loss positioned 50% below entry would lose half the position size in one trade, potentially destroying the strategy's expected value.
Example Scenario:
priceOpen = 42000, priceStopLoss = 20000 (52.4% loss)Diagram: Stop Loss Distance Validation Flow
The validation calculates the percentage distance between entry price and stop loss target, then compares it against CC_MAX_STOPLOSS_DISTANCE_PERCENT. For long positions, SL must be below entry. For short positions, SL must be above entry.
Long Position Validation (src/client/ClientStrategy.ts:189-199):
const slDistancePercent = ((signal.priceOpen - signal.priceStopLoss) / signal.priceOpen) * 100;
if (slDistancePercent > GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT) {
errors.push(
`Long: StopLoss too far from priceOpen (${slDistancePercent.toFixed(3)}%). ` +
`Maximum distance: ${GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT}% to protect capital. ` +
`Current: SL=${signal.priceStopLoss}, Open=${signal.priceOpen}`
);
}
Short Position Validation (src/client/ClientStrategy.ts:280-290):
const slDistancePercent = ((signal.priceStopLoss - signal.priceOpen) / signal.priceOpen) * 100;
if (slDistancePercent > GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT) {
errors.push(
`Short: StopLoss too far from priceOpen (${slDistancePercent.toFixed(3)}%). ` +
`Maximum distance: ${GLOBAL_CONFIG.CC_MAX_STOPLOSS_DISTANCE_PERCENT}% to protect capital. ` +
`Current: SL=${signal.priceStopLoss}, Open=${signal.priceOpen}`
);
}
The default value of 20% represents a reasonable maximum loss per signal:
5-10% for conservative strategies100+%) in productionSignals with excessively long minuteEstimatedTime can create strategy deadlock by occupying risk limits indefinitely. If a signal expects to remain active for 30+ days, it blocks new signals from being generated, effectively freezing the strategy.
Example Scenario:
minuteEstimatedTime = 50000 minutes (34.7 days)maxConcurrentPositions = 3Diagram: Signal Lifetime Validation Flow
The validation compares minuteEstimatedTime directly against CC_MAX_SIGNAL_LIFETIME_MINUTES. Error messages include human-readable day conversions for clarity.
Validation Implementation (src/client/ClientStrategy.ts:306-316):
if (signal.minuteEstimatedTime > GLOBAL_CONFIG.CC_MAX_SIGNAL_LIFETIME_MINUTES) {
const days = (signal.minuteEstimatedTime / 60 / 24).toFixed(1);
const maxDays = (GLOBAL_CONFIG.CC_MAX_SIGNAL_LIFETIME_MINUTES / 60 / 24).toFixed(0);
errors.push(
`minuteEstimatedTime too large (${signal.minuteEstimatedTime} minutes = ${days} days). ` +
`Maximum: ${GLOBAL_CONFIG.CC_MAX_SIGNAL_LIFETIME_MINUTES} minutes (${maxDays} days) to prevent strategy deadlock. ` +
`Eternal signals block risk limits and prevent new trades.`
);
}
The default value of 1440 minutes (1 day):
999999) in productionThe following diagram shows how validation parameters integrate into the signal generation pipeline:
Diagram: Complete Signal Validation Flow with Validation Parameters
The validation occurs in VALIDATE_SIGNAL_FN at src/client/ClientStrategy.ts:45-330. The function accumulates all validation errors into an array, then throws a single descriptive error if any validations fail. This provides comprehensive feedback to strategy developers.
Key Validation Stages:
| Stage | Lines | Validation Logic | Configuration Used |
|---|---|---|---|
| Required Fields | 48-69 | Checks id, exchangeName, strategyName, symbol, position, _isScheduled are non-null/empty |
N/A |
| NaN/Infinity Protection | 71-96 | Validates currentPrice, priceOpen, priceTakeProfit, priceStopLoss are finite numbers |
N/A |
| Positive Price Validation | 99-109 | Ensures all prices are positive (> 0) | N/A |
| Long Position Logic | 112-199 | For position === "long": validates TP > Open > SL, checks all distance constraints |
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT, CC_MIN_STOPLOSS_DISTANCE_PERCENT, CC_MAX_STOPLOSS_DISTANCE_PERCENT |
| Short Position Logic | 202-290 | For position === "short": validates SL > Open > TP, checks all distance constraints |
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT, CC_MIN_STOPLOSS_DISTANCE_PERCENT, CC_MAX_STOPLOSS_DISTANCE_PERCENT |
| Time Parameter Validation | 293-323 | Validates minuteEstimatedTime, scheduledAt, pendingAt are positive integers |
CC_MAX_SIGNAL_LIFETIME_MINUTES |
| Error Aggregation | 325-330 | Throws single error with newline-separated list of all failures | N/A |
Validation parameters can be modified at runtime using the setConfig() function. This is typically done during initialization or in test environments.
import { setConfig } from 'backtest-kit';
// Example: Conservative configuration for stable markets
setConfig({
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: 1.0, // Require 1.0% minimum profit (higher than default 0.5%)
CC_MIN_STOPLOSS_DISTANCE_PERCENT: 0.8, // Require 0.8% min SL buffer (higher than default 0.5%)
CC_MAX_STOPLOSS_DISTANCE_PERCENT: 10, // Allow max 10% stop loss (tighter than default 20%)
CC_MAX_SIGNAL_LIFETIME_MINUTES: 720, // Max 12 hours per signal (shorter than default 24h)
});
| Use Case | Configuration | Rationale |
|---|---|---|
| Disable Validation (Testing) | CC_MIN_TP: 0, CC_MIN_SL: 0, CC_MAX_SL: 100, CC_MAX_LIFETIME: 999999 |
Allows testing edge cases without validation constraints |
| Conservative Trading | CC_MIN_TP: 1.0%, CC_MIN_SL: 0.8%, CC_MAX_SL: 5%, CC_MAX_LIFETIME: 360 min |
Tighter constraints for risk-averse strategies and stable markets |
| Default (Balanced) | CC_MIN_TP: 0.5%, CC_MIN_SL: 0.5%, CC_MAX_SL: 20%, CC_MAX_LIFETIME: 1440 min |
Standard configuration covering fees + slippage + margin, reasonable max loss |
| Volatile Markets | CC_MIN_TP: 0.8%, CC_MIN_SL: 1.0%, CC_MAX_SL: 30%, CC_MAX_LIFETIME: 2880 min |
Higher margins to handle volatility swings, wider SL tolerance |
| Long-term Holding | CC_MIN_TP: 2%, CC_MIN_SL: 1.0%, CC_MAX_SL: 20%, CC_MAX_LIFETIME: 10080 min |
Larger minimum profits for multi-day positions, allow 1-week signals |
Test files disable validation by default to isolate signal logic testing from validation constraints:
// Test environment typically disables validation globally
setConfig({
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: 0, // No TP distance check
CC_MIN_STOPLOSS_DISTANCE_PERCENT: 0, // No min SL distance check
CC_MAX_STOPLOSS_DISTANCE_PERCENT: 100, // Allow any SL
CC_MAX_SIGNAL_LIFETIME_MINUTES: 999999, // No lifetime limit
});
Individual tests re-enable specific validations to verify error handling:
// Example: Testing TP distance validation in isolation
test("Micro-profit validation", async () => {
setConfig({
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT: 0.5, // Enable TP validation with default value
});
// Signal with TP too close to Open should throw validation error
await addStrategy({
strategyName: "micro-profit-test",
interval: "5m",
getSignal: async () => ({
position: "long",
priceOpen: 42000,
priceTakeProfit: 42010, // 0.024% - below 0.5% threshold
priceStopLoss: 41000,
minuteEstimatedTime: 60,
}),
});
// Expected: Signal rejected with "TakeProfit too close to priceOpen" error
});
Validation failures produce detailed error messages that help strategy developers diagnose issues:
Long: TakeProfit too close to priceOpen (0.024%).
Minimum distance: 0.5% to cover trading fees.
Current: TP=42010, Open=42000
The error includes:
0.024%0.3%TP=42010, Open=42000Long: StopLoss too close to priceOpen (0.024%).
Minimum distance: 0.5% to avoid instant stop out on market volatility.
Current: SL=41990, Open=42000
Long: StopLoss too far from priceOpen (52.381%).
Maximum distance: 20% to protect capital.
Current: SL=20000, Open=42000
minuteEstimatedTime too large (50000 minutes = 34.7 days).
Maximum: 1440 minutes (1 days) to prevent strategy deadlock.
Eternal signals block risk limits and prevent new trades.
Multiple validation failures are combined into a single error message:
Invalid signal for long position:
Long: TakeProfit too close to priceOpen (0.024%). Minimum distance: 0.5% to cover trading fees. Current: TP=42010, Open=42000
Long: StopLoss too close to priceOpen (0.024%). Minimum distance: 0.5% to avoid instant stop out on market volatility. Current: SL=41990, Open=42000
Long: StopLoss too far from priceOpen (52.381%). Maximum distance: 20% to protect capital. Current: SL=20000, Open=42000
minuteEstimatedTime too large (50000 minutes = 34.7 days). Maximum: 1440 minutes (1 days) to prevent strategy deadlock. Eternal signals block risk limits and prevent new trades.
This comprehensive error output allows developers to fix all validation issues in a single iteration, reducing trial-and-error debugging cycles.
The table below summarizes default values, their safety guarantees, and when to adjust them:
| Parameter | Default | Safety Guarantee | When to Increase | When to Decrease |
|---|---|---|---|---|
CC_MIN_TAKEPROFIT_DISTANCE_PERCENT |
0.5% | Prevents fee-eating micro-profits (0.4% total costs + 0.1% margin) | Higher fee exchanges (1%+ for VIP tiers), require larger minimum profit (2-5%) | Zero-fee maker rebate accounts (0.2%), testing environments |
CC_MIN_STOPLOSS_DISTANCE_PERCENT |
0.5% | Prevents instant stop-outs from normal market volatility and spread fluctuations | High-volatility crypto markets (1-2%), assets with wide spreads | Stable forex major pairs with tight spreads (0.2-0.3%), low volatility periods |
CC_MAX_STOPLOSS_DISTANCE_PERCENT |
20% | Prevents catastrophic single-trade losses (max 20% position loss) | High-volatility assets requiring wide stops (crypto 30-40%), low-liquidity markets | Stable forex pairs (10-15%), conservative risk management (5-10%) |
CC_MAX_SIGNAL_LIFETIME_MINUTES |
1440 (1 day) | Prevents strategy deadlock from eternal signals blocking risk limits | Swing trading strategies (10080 = 1 week), position trading (43200 = 1 month) | Scalping strategies (60-120 min), intraday mean reversion (240-480 min) |
These validation parameters work in conjunction with risk profile limits (see Risk Profiles):
Diagram: Validation Parameters as First Line of Defense
Validation parameters act as the first line of defense, rejecting signals before they reach the risk management layer. This prevents invalid signals from consuming risk limit slots or triggering custom validations.