Testing & Validation
Learn how to test and validate your custom strategies before deployment.
Overview
Testing is crucial to ensure your strategy works correctly and doesn't have bugs that could cause unexpected trades or losses. TradeStaq provides multiple ways to test your strategies.
Code Validation
Before a strategy can run, it goes through validation:
Syntax Checking
The platform checks for JavaScript syntax errors:
// This will fail validation
if (rsi < 30 { // Missing closing parenthesis
td.trade.buy();
}
// Correct syntax
if (rsi < 30) {
td.trade.buy();
}
Forbidden Patterns
Certain patterns are blocked for security:
// FORBIDDEN - will fail validation
eval('code'); // No dynamic code execution
new Function('code'); // No function constructor
fetch('https://...'); // No network access
require('fs'); // No Node.js modules
process.exit(); // No process control
Resource Limits
Strategies must operate within platform resource limits for code size, execution time, and memory usage. These limits ensure fair resource sharing across all users.
Data Validation
Before each strategy spin, the platform validates that critical market data is available and valid. If validation fails, the spin is skipped and an error is logged.
Critical Data (Spin will fail if invalid)
| Data | Validation |
|---|---|
td.market.price | Must be > 0, not NaN |
td.market.symbol | Must be non-empty |
td.market.timeframe | Must be non-empty |
td.market.exchange | Must be non-empty |
td.market.bid / ask | Must be > 0 |
td.market.candles | Must not be empty |
td.account.balance | Must be > 0 |
td.indicators.rsi | Must be 0-100 |
td.indicators.atr | Must be >= 0 |
Warnings (Spin continues with warning)
| Data | Warning Condition |
|---|---|
td.market.candles | Less than 15 candles (indicators may be unreliable) |
td.market.bid/ask | Bid >= ask (unusual spread) |
| OHLC values | High < low (invalid candle) |
| Bollinger Bands | Upper < lower |
Handling Sparse Data
For newly listed coins with limited history:
// Check if we have enough data for your strategy
if (td.market.candles.length < 50) {
td.utils.log('Limited candle history, using simplified logic');
// Use simpler entry logic or skip
return;
}
Bots in on_bar_close execution mode need at least one fully-closed candle before they can run at all. During the warm-up window (fresh bot, feed gap, cache rebuild) the spin returns no_signal with a clear skip reason rather than silently falling back to continuous behavior. Nothing for you to handle in the strategy — it just means the first signal arrives one closed bar later than deploy time.
Consecutive Failures
If a bot encounters 5 consecutive validation failures, it will be automatically paused. Check your bot's activity log for error details.
Test Endpoint
Test your strategy without deploying it:
POST /api/tradedroid/strategies/{id}?action=test
Content-Type: application/json
{
"symbol": "BTC/USDT",
"timeframe": "1h",
"basePrice": 50000,
"config": {
"RSI_OVERSOLD": 25,
"STOP_LOSS_PCT": 3
}
}
Test Response
{
"status": "success",
"signals": [
{
"type": "buy",
"amount": 100,
"amountPercent": true,
"stopLoss": 48500,
"takeProfit": 52000,
"reason": "RSI oversold at 23.45"
}
],
"logs": [
{
"message": "Strategy tick",
"data": { "rsi": 23.45, "price": 50000 },
"timestamp": 1699999999999
}
],
"state": {
"previousRsi": 23.45
},
"executionTimeMs": 45,
"memoryUsedMb": 12.5
}
Validation Errors
Common Errors
| Error | Cause | Solution |
|---|---|---|
| Syntax error | JavaScript syntax issue | Check for missing brackets, semicolons |
| Timeout exceeded | Execution took too long | Optimize loops, reduce complexity |
| Memory exceeded | Used too much memory | Reduce data storage, optimize arrays |
| Infinite loop | while(true) or similar | Add loop exit conditions |
| Code too large | Strategy file is too big | Reduce comments, consolidate code |
| Forbidden pattern | Security violation | Remove eval, fetch, require, etc. |
Debugging Syntax Errors
// Error: Unexpected token
const rsi = td.indicators.rsi
if (rsi < 30) { // Missing semicolon above
td.trade.buy();
}
// Fixed
const rsi = td.indicators.rsi;
if (rsi < 30) {
td.trade.buy();
}
Debugging Infinite Loops
// BAD - infinite loop
while (true) {
// Process forever
}
// GOOD - loop with exit condition
let iterations = 0;
while (condition && iterations < 1000) {
// Process
iterations++;
}
Paper Trading Testing
The safest way to test strategies with real market data:
Setup
- Create a paper trading exchange
- Create a bot using your strategy
- Link bot to paper exchange
- Let it run for observation period
What to Monitor
- Signal frequency: Is it trading too often or too rarely?
- Entry timing: Are entries at expected indicator levels?
- Exit behavior: Are positions closing as expected?
- Error messages: Check logs for any issues
Recommended Testing Period
| Strategy Type | Minimum Test Period |
|---|---|
| Scalping (1m-5m) | 3-7 days |
| Day trading (15m-1h) | 1-2 weeks |
| Swing trading (4h-1d) | 2-4 weeks |
| Position trading (1w) | 1-2 months |
Backtesting
Test against historical data:
Running a Backtest
- Navigate to Dashboard → Backtests
- Click New Backtest
- Configure:
- Strategy selection
- Trading pair
- Timeframe
- Date range
- Initial balance
- Position sizing
- Run backtest
Interpreting Results
Key metrics to evaluate:
| Metric | Good Value | Description |
|---|---|---|
| Win Rate | > 40% | Percentage of winning trades |
| Profit Factor | > 1.5 | Gross profit / gross loss |
| Max Drawdown | < 20% | Largest peak-to-trough decline |
| Sharpe Ratio | > 1.0 | Risk-adjusted returns |
| Total Trades | 30+ | Statistical significance |
Backtest Limitations
- No slippage simulation: Real execution may differ
- Idealized fills: Assumes orders always fill at expected price
- Past ≠ future: Historical performance doesn't guarantee results
- Curve fitting risk: Over-optimized parameters may not generalize
Unit Testing Patterns
Test individual components of your strategy:
// Test helper function
function testCalculation() {
const testCases = [
{ input: 100, expected: 98 },
{ input: 200, expected: 196 },
];
for (const test of testCases) {
const result = calculateStopLoss(test.input, 2);
if (result !== test.expected) {
td.utils.log('TEST FAILED', {
input: test.input,
expected: test.expected,
got: result
});
}
}
}
// Run tests on first spin
if (td.meta.spinCount === 1) {
testCalculation();
}
Logging for Debugging
Use comprehensive logging during testing:
// Verbose logging for testing
const DEBUG = td.config.getBoolean('DEBUG_MODE', true);
if (DEBUG) {
td.utils.log('Strategy state', {
spinCount: td.meta.spinCount,
price: td.market.price,
rsi: td.indicators.rsi,
macd: td.indicators.macd,
hasPosition: td.position.hasPosition,
pnl: td.position.pnl,
state: td.state.getAll()
});
}
// Entry decision logging
if (entryCondition) {
td.utils.log('ENTRY SIGNAL', {
reason: 'RSI oversold',
rsi: td.indicators.rsi,
price: td.market.price,
stopLoss: calculatedSL,
takeProfit: calculatedTP
});
}
Pre-deployment Checklist
Before going live:
- Code passes validation without errors
- Strategy tested on paper exchange for minimum period
- Backtest shows acceptable performance metrics
- All parameters have sensible defaults
- Edge cases handled (missing data, extreme values)
- Stop losses are always set
- Position checks prevent duplicate entries
- Logging is appropriate (not excessive in production)
- Risk per trade is within acceptable limits
Going Live Safely
Start Small
// Use smaller position sizes initially
const isNewStrategy = td.state.get('totalTrades', 0) < 10;
const positionSize = isNewStrategy ? 25 : 100; // 25% until proven
td.trade.buy({ amountPercent: positionSize });
Monitor Closely
First week of live trading:
- Check bot status multiple times daily
- Review each trade in logs
- Compare to expected behavior
- Be ready to pause if issues arise
Gradual Scaling
- Week 1-2: 25% position size
- Week 3-4: 50% position size
- Month 2+: Full position size (if performing well)
Next Steps
- Best Practices - Risk management guidelines
- Examples - Tested strategy examples
- Publishing - Share your strategies