The Old Code Still Had a Switch
The dangerous code path was not new.
That is the part I keep coming back to.
On August 1, 2012, Knight Capital Americas experienced a failure in an automated equity order router called SMARS. According to the SEC’s later settled order, SMARS processed 212 small retail parent orders, routed millions of child orders over about 45 minutes, obtained more than 4 million executions in 154 stocks, and traded more than 397 million shares. Knight ended with unwanted positions of about $3.5 billion net long and $3.15 billion net short, and ultimately lost over $460 million.1
Knight’s own August 2, 2012 press release put the realized pre-tax loss at approximately $440 million and said the problem was related to installation of trading software.2 The SEC order came later, with more detail. The order was settled without Knight admitting or denying the findings, except for jurisdiction and related procedural matters.1
So the evidence standard here is:
facts below are SEC findings unless I mark them as interpretation
The interpretation is mine:
old production code is still production code if anything can call it
The Flag Was Reused
Knight was preparing SMARS for the NYSE Retail Liquidity Program, scheduled to start on August 1, 2012. The new RLP-related code was meant to replace unused code for an older SMARS feature called Power Peg.1
Power Peg was not supposed to be active. But according to the SEC, the old code was still present and callable. The new RLP work also reused a flag that had formerly activated Power Peg. Knight intended that flag to activate the new RLP logic after the old code was removed.
That is already a sharp interface:
same flag value
different production meaning
depends on every server receiving the same code state
The older code had another problem. In 2005, Knight had moved a cumulative quantity function to an earlier point in the SMARS code sequence. That function had originally helped Power Peg stop sending child orders after the parent order was filled. The SEC found that Knight did not retest Power Peg after moving the function.1
So Power Peg was both unused and unproven.
That combination is worse than obviously broken code. Obviously broken code attracts attention. Dormant code can keep the badge of production legitimacy while quietly losing the test coverage, operational familiarity, and invariants that once made it safe.
One Server Missed the Deploy
Beginning July 27, 2012, Knight deployed the new code to SMARS servers in stages. One technician did not copy the new code to one of eight SMARS servers. The SEC found there was no second-technician review requirement, no one realized the eighth server still had Power Peg and lacked the new RLP code, and Knight had no written procedure requiring that review.1
On August 1, orders carrying the repurposed flag reached the system. Seven servers processed them correctly. The eighth server interpreted the flag as a Power Peg activation. Because the cumulative quantity information no longer flowed as that old code expected, the server repeatedly sent child orders even after parent orders had already been filled.1
The failure shape was not:
bad code executes once
It was:
bad code executes
does not observe completion
keeps producing marketable output
That is a feedback failure. The order router kept asking the market to do work after another part of the system already knew the customer-sized job was done.
The Alerts Were Not Alerts
There was a warning before the open.
The SEC found that beginning around 8:01 a.m. Eastern, Knight’s internal system generated 97 automated emails called BNET rejects. These messages referenced SMARS and identified a Power Peg disabled error. They were caused by the code deployment failure and were sent before the 9:30 a.m. market open.1
But they were not designed as system alerts, and Knight personnel generally did not review them when received. They were not acted on before the open and were not used to diagnose the problem after the open.1
This is a useful distinction:
message delivered != alert received
alert received != incident response triggered
A production system can emit the truth and still fail operationally if the truth lands in a channel nobody treats as binding.
The Control Was After the Damage
The SEC’s market access rule, Rule 15c3-5, requires broker-dealers with market access to establish documented risk controls and supervisory procedures reasonably designed to manage financial, regulatory, and other risks. Among other things, the controls must be designed to prevent erroneous orders and orders that exceed pre-set credit or capital thresholds.3
The SEC order says Knight had controls before orders reached SMARS, but did not have adequate controls inside SMARS to prevent erroneous orders from leaving the router. One missing control was the simple-sounding comparison of orders leaving SMARS with orders entering it.1 In other words:
router output should be accountable to router input
Knight also had a post-execution position monitor called PMON. But PMON relied on human monitoring, was not linked to order entry in a way that could automatically stop orders at pre-set capital thresholds, did not display account limits, and experienced delays during high-volume events.1
Observation after execution is not the same as prevention before submission.
The SEC press release later summarized this as the agency’s first enforcement action under the market access rule and said Knight agreed to pay a $12 million penalty.4 The formal order also censured Knight, imposed a cease-and-desist order, and required undertakings including an independent consultant review.1
Lab: A Stale Server With a Loop
The lab below is not a reconstruction of Knight’s real order book. It does not model NYSE opening auctions, venue routing, price impact, order types, liquidation strategy, stock-level heterogeneity, Regulation SHO, or the exact sequence of human response.
It is a calibrated toy model of one narrow mechanism:
- a fleet of eight routers;
- one or more stale servers;
- a reused flag that can activate old code on stale servers;
- parent orders that enter the bad path;
- a loop that emits child executions until something stops it;
- optional controls that act before the full exposure horizon.
The default calibration uses the SEC-scale aggregates:
bad parent orders: 212
exposure window: 45 minutes
executions: about 4.0 million
shares: about 397 million
loss scale: about $460 million
The model derives average executions per parent per minute, shares per execution, gross dollars per share, and loss dollars per share from those aggregate facts. That makes the sliders interpretable, but it also means the lab is deliberately coarse.
What the Controls Change
With the defaults, the toy produces:
child executions: 3,997,260
shares: 396.7 million
gross scale: $6.65 billion
loss scale: $459.7 million
amplification: 18,855 child executions per bad parent order
Turn on deployment verification and the stale server never becomes an active market path in the toy. Turn on the router output cap with a cap of 25 child executions per parent and the toy stops at 5,300 child executions. Turn on a $20 million capital guard and the toy stops after about 1.96 active minutes.
Those exact counterfactual numbers are not historical claims. They are model outputs. The historical claim is narrower and better supported: the SEC found that Knight lacked adequate controls at the router output point, lacked automated capital-threshold controls that could prevent order entry, and lacked adequate deployment and incident-response procedures.4
The systems lesson is that each control answers a different failure mode:
deployment parity check -> do all production nodes mean the same thing?
router output cap -> is output proportional to input?
capital guard -> can exposure stop without a meeting?
incident procedure -> who is allowed to halt while diagnosis is incomplete?
The last line matters. The SEC found that while Knight staff tried to diagnose the problem in the live trading environment, Knight remained connected and kept sending orders. In one attempted fix, Knight removed the new RLP code from the seven servers where it had been deployed correctly, which worsened the problem by allowing more incoming parent orders to activate Power Peg on those servers.1
That is what an incident runbook is for. It is not a poem about calmness. It is a pre-authorized answer to the question:
when we do not yet understand the bug,
which outputs are no longer allowed?
Reproducibility Check
The lab exports the same calculation for Node:
const lab = require("./assets/js/knight-deploy-lab.js");
const EXPECTED_TOTALS = {
rows: 6,
passed: 6,
total: 6,
passedChecks: 39,
totalChecks: 39
};
const EXPECTED_CASE_ROWS = [
"1:1:212:45:419:0:0:40:0:50:3997260:396.7:459.7:45.00:18855.00:9/9:true",
"2:1:212:45:419:1:0:40:0:50:0:0.0:0.0:0.00:0.00:6/6:true",
"3:1:212:45:419:0:1:25:0:50:5300:0.5:0.6:0.06:25.00:6/6:true",
"4:1:212:45:419:0:0:40:1:20:173913:17.3:20.0:1.96:820.34:6/6:true",
"5:0:212:45:419:0:0:40:0:50:0:0.0:0.0:0.00:0.00:6/6:true",
"6:2:120:20:350:0:0:40:0:50:1680000:166.7:193.2:20.00:7000.00:6/6:true"
];
const EXPECTED_BASE_CHECKS = [
{ name: "outputs are finite and non-negative", passed: true },
{ name: "deployment check prevents stale-server activation", passed: true },
{ name: "zero stale servers cause zero erroneous executions", passed: true },
{ name: "actual executions do not exceed raw loop", passed: true },
{ name: "router guard enforces per-parent cap", passed: true },
{ name: "capital guard enforces configured loss bound", passed: true }
];
const EXPECTED_BASELINE_CHECKS = [
...EXPECTED_BASE_CHECKS,
{ name: "default calibration is near SEC execution count", passed: true },
{ name: "default calibration is near SEC share count", passed: true },
{ name: "default calibration is near SEC loss scale", passed: true }
];
function sameJson(actual, expected) {
return JSON.stringify(actual) === JSON.stringify(expected);
}
function rowDrifts(actualRows, expectedRows) {
const drifts = [];
const rows = Math.max(actualRows.length, expectedRows.length);
for (let index = 0; index < rows; index += 1) {
const actual = index < actualRows.length ? actualRows[index] : null;
const expected = index < expectedRows.length ? expectedRows[index] : null;
if (!sameJson(actual, expected)) {
drifts.push({ index, actual, expected });
}
}
return drifts;
}
const audit = lab.auditGrid();
const failed = audit.cases.filter(
(row) => !row.passed || row.passedChecks !== row.totalChecks
);
const totals = {
rows: audit.cases.length,
passed: audit.passed,
total: audit.total,
passedChecks: audit.passedChecks,
totalChecks: audit.totalChecks
};
const caseRows = audit.cases.map((row, index) => [
index + 1,
row.params.staleServers,
row.params.badParentOrders,
row.params.exposureMinutes,
row.params.executionsPerParentMinute,
row.params.deploymentCheck,
row.params.routerGuard,
row.params.outputCap,
row.params.capitalGuard,
row.params.capitalLimit,
Math.round(row.executions),
(row.shares / 1e6).toFixed(1),
(row.estimatedLoss / 1e6).toFixed(1),
row.activeMinutes.toFixed(2),
row.amplification.toFixed(2),
`${row.passedChecks}/${row.totalChecks}`,
row.passed
].join(":"));
const checkRows = audit.cases.map((row, index) => ({
caseId: index + 1,
checks: row.checks.map((check) => ({
name: check.name,
passed: check.passed
}))
}));
const expectedCheckRows = EXPECTED_CASE_ROWS.map((row) => {
const caseId = Number(row.split(":")[0]);
return {
caseId,
checks: caseId === 1 ? EXPECTED_BASELINE_CHECKS : EXPECTED_BASE_CHECKS
};
});
const caseRowDrifts = rowDrifts(caseRows, EXPECTED_CASE_ROWS);
const checkRowDrifts = rowDrifts(checkRows, expectedCheckRows);
const driftShape = {
totals,
caseRowDrifts,
checkRowDrifts
};
const shapeErrors = [
sameJson(totals, EXPECTED_TOTALS) ? null : "totals",
caseRowDrifts.length === 0 ? null : "caseRows",
checkRowDrifts.length === 0 ? null : "checkRows"
].filter(Boolean);
if (shapeErrors.length) {
throw new Error(
`Knight audit shape drifted in ${shapeErrors.join(", ")}:\n` +
JSON.stringify(driftShape, null, 2)
);
}
const summary =
`${audit.passed}/${audit.total} cases and ` +
`${audit.passedChecks}/${audit.totalChecks} checks passed`;
if (
failed.length ||
!audit.ok ||
audit.passed !== audit.total ||
audit.passedChecks !== audit.totalChecks
) {
throw new Error(`${summary}: ${JSON.stringify(failed, null, 2)}`);
}
console.table(audit.cases.map((row) => ({
stale: row.params.staleServers,
check: row.params.deploymentCheck,
router: row.params.routerGuard,
capital: row.params.capitalGuard,
checks: `${row.passedChecks}/${row.totalChecks}`,
executions: Math.round(row.executions),
sharesM: (row.shares / 1e6).toFixed(1),
lossM: (row.estimatedLoss / 1e6).toFixed(1),
minutes: row.activeMinutes.toFixed(2),
passed: row.passed
})));
console.log(summary);
The audit checks that the default calibration is close to the SEC aggregate execution, share, and loss scale; that a deployment check prevents stale-server activation; that a zero-stale-server case creates no erroneous executions; and that router and capital guards enforce their configured bounds. The current grid covers six counterfactual scenarios and 39 named checks.
The Smaller Lesson
It is tempting to file Knight under “bad deploy” and move on. That is too small.
A missed server mattered because old code was still callable. A reused flag mattered because different servers disagreed about its meaning. The alert emails mattered less than the absence of an operational channel that made them interruptive. The position monitor mattered less than the fact that it could not prevent order entry. Diagnosis mattered less than the decision to remain connected while diagnosis was still uncertain.
The useful abstraction is:
a production system is the code paths that can still affect the outside world
Not the code paths you meant to use.
Not the code paths you remember.
Not the code paths that passed their last test in 2003.
If the switch still reaches them, they are alive.
-
U.S. Securities and Exchange Commission, “In the Matter of Knight Capital Americas LLC,” Exchange Act Release No. 70694, October 16, 2013. The order describes SMARS, the Power Peg/RLP deployment, the 212 parent orders, more than 4 million executions, more than 397 million shares, the long and short positions, and the risk-control findings. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12
-
Knight Capital Group, Inc., “Knight Capital Group Provides Update Regarding August 1st Disruption to Routing in NYSE-Listed Securities”, furnished as Exhibit 99.1 on August 2, 2012. Knight reported an approximately $440 million realized pre-tax loss and said the software issue had been removed from its systems. ↩
-
U.S. Securities and Exchange Commission, “Risk Management Controls for Brokers or Dealers with Market Access,” Exchange Act Release No. 63241, November 3, 2010. The adopting release explains Rule 15c3-5’s requirement for broker-dealer market-access risk controls, including controls designed to limit financial exposure and prevent erroneous orders. ↩
-
U.S. Securities and Exchange Commission, “SEC Charges Knight Capital With Violations of Market Access Rule”, October 16, 2013. The press release summarizes the SEC’s findings, calls the case the first enforcement action under the market access rule, and states the $12 million settlement penalty. ↩ ↩2