Signal Backtester
Problem Description
Backtest a trading signal against historical price data. Each row contains a timestamp, price, and signal value (-1 for sell, 0 for flat, 1 for buy). Execute trades based on signal changes and compute performance metrics.
Compute total P&L, number of trades, win rate, maximum drawdown, Sharpe ratio, Sortino ratio, and maximum consecutive wins. Output 7 aggregate values -- 5 floats and 2 integers.
Tier System & Performance Targets
| LANGUAGE | GOLD | SILVER | BRONZE |
|---|---|---|---|
| C++ / Rust | < 300ms | < 1000ms | < 5000ms |
| Java | < 600ms | < 2000ms | < 10000ms |
| Python | < 3000ms | < 10000ms | < 45000ms |
Evaluation Criteria
Correctness: Your program outputs 7 values. 5 float values are compared with tolerance 0.01, and 2 integer values must match exactly. All 7 must be correct to pass.
Performance: Wall-clock time to process all rows determines tier placement.
Input Format
Read from stdin. The first line is the number of rows N. The next N lines each contain a comma-separated row:
timestamp,price,signal
Where:
timestamp: Integer timestamp (monotonically increasing)price: Asset price (float)signal: Trading signal ---1(sell),0(flat/no position), or1(buy)
Output Format
Output exactly 7 lines to stdout:
TOTAL_PNL
NUM_TRADES
WIN_RATE
MAX_DRAWDOWN
SHARPE_RATIO
SORTINO_RATIO
MAX_CONSECUTIVE_WINS
Where:
TOTAL_PNL: Total profit and loss (float, 6 decimal places)NUM_TRADES: Total number of round-trip trades (integer)WIN_RATE: Fraction of winning trades (float, 6 decimal places)MAX_DRAWDOWN: Maximum peak-to-trough drawdown (float, 6 decimal places)SHARPE_RATIO: Annualized Sharpe ratio (float, 6 decimal places)SORTINO_RATIO: Annualized Sortino ratio (float, 6 decimal places)MAX_CONSECUTIVE_WINS: Longest streak of consecutive winning trades (integer)
Example
Input
8
1000,100.00,0
1001,101.50,1
1002,103.00,1
1003,102.00,-1
1004,99.50,-1
1005,100.25,0
1006,98.00,1
1007,101.00,0
Output
4.500000
3
0.666667
0.025000
1.250000
1.850000
2
Line 1: Total P&L from all trades. Line 2: 3 round-trip trades (integer). Line 3: 2 of 3 trades were winners. Line 4: Max drawdown. Line 5-6: Sharpe and Sortino ratios. Line 7: Longest winning streak (integer).
Submission Rules
- Your program receives input via stdin
- Output 7 lines to stdout (5 floats with 6 decimal places, 2 integers)
- Program must complete within 120 seconds
- Memory usage must not exceed 2GB
- Single-file solutions only
- Java class must be named
Solution
Starter Templates
C++
#include <cstdio>
#include <cmath>
#include <vector>
int main() {
int N;
scanf("%d", &N);
long long ts;
double price;
int signal;
double total_pnl = 0;
int num_trades = 0, max_consec_wins = 0;
// Track trades, returns, drawdown...
for (int i = 0; i < N; i++) {
scanf("%lld,%lf,%d", &ts, &price, &signal);
// Implement: execute trades on signal changes
// Track P&L, wins/losses, drawdown, returns for Sharpe/Sortino
}
double win_rate = 0, max_dd = 0, sharpe = 0, sortino = 0;
// Calculate final metrics
printf("%.6f\n", total_pnl);
printf("%d\n", num_trades);
printf("%.6f\n", win_rate);
printf("%.6f\n", max_dd);
printf("%.6f\n", sharpe);
printf("%.6f\n", sortino);
printf("%d\n", max_consec_wins);
return 0;
}
Python
import sys
import math
N = int(input())
total_pnl = 0.0
num_trades = 0
wins = 0
max_consec_wins = 0
consec_wins = 0
max_drawdown = 0.0
returns = []
prev_signal = 0
entry_price = 0.0
for _ in range(N):
parts = input().split(',')
ts = int(parts[0])
price = float(parts[1])
signal = int(parts[2])
# Implement: execute trades on signal changes
# Track P&L, wins/losses, drawdown, returns for Sharpe/Sortino
prev_signal = signal
win_rate = wins / num_trades if num_trades > 0 else 0.0
# Calculate Sharpe and Sortino from returns
sharpe = 0.0
sortino = 0.0
print(f"{total_pnl:.6f}")
print(num_trades)
print(f"{win_rate:.6f}")
print(f"{max_drawdown:.6f}")
print(f"{sharpe:.6f}")
print(f"{sortino:.6f}")
print(max_consec_wins)
Rust
use std::io::{self, BufRead, Write, BufWriter};
fn main() {
let stdin = io::stdin();
let stdout = io::stdout();
let mut out = BufWriter::new(stdout.lock());
let mut lines = stdin.lock().lines();
let n: usize = lines.next().unwrap().unwrap().trim().parse().unwrap();
let mut total_pnl: f64 = 0.0;
let mut num_trades: i32 = 0;
let mut max_consec_wins: i32 = 0;
// Track trades, returns, drawdown...
for _ in 0..n {
let line = lines.next().unwrap().unwrap();
let parts: Vec<&str> = line.trim().split(',').collect();
let _ts: i64 = parts[0].parse().unwrap();
let price: f64 = parts[1].parse().unwrap();
let signal: i32 = parts[2].parse().unwrap();
// Implement: execute trades on signal changes
}
let win_rate: f64 = 0.0;
let max_dd: f64 = 0.0;
let sharpe: f64 = 0.0;
let sortino: f64 = 0.0;
writeln!(out, "{:.6}", total_pnl).unwrap();
writeln!(out, "{}", num_trades).unwrap();
writeln!(out, "{:.6}", win_rate).unwrap();
writeln!(out, "{:.6}", max_dd).unwrap();
writeln!(out, "{:.6}", sharpe).unwrap();
writeln!(out, "{:.6}", sortino).unwrap();
writeln!(out, "{}", max_consec_wins).unwrap();
}
Java
import java.util.*;
import java.io.*;
public class Solution {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine().trim());
double totalPnl = 0;
int numTrades = 0, maxConsecWins = 0;
// Track trades, returns, drawdown...
for (int i = 0; i < N; i++) {
String[] parts = br.readLine().trim().split(",");
long ts = Long.parseLong(parts[0]);
double price = Double.parseDouble(parts[1]);
int signal = Integer.parseInt(parts[2]);
// Implement: execute trades on signal changes
}
double winRate = 0, maxDd = 0, sharpe = 0, sortino = 0;
System.out.printf("%.6f%n", totalPnl);
System.out.printf("%d%n", numTrades);
System.out.printf("%.6f%n", winRate);
System.out.printf("%.6f%n", maxDd);
System.out.printf("%.6f%n", sharpe);
System.out.printf("%.6f%n", sortino);
System.out.printf("%d%n", maxConsecWins);
}
}