Manual leg: may join a lane on the same instrument and side, never the other
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b5ed423145
commit
675b4c89f8
+20
-5
@@ -1373,8 +1373,9 @@ async function copyTradeLocked(lease: number, lane: string): Promise<unknown[]>
|
|||||||
* The one rule it overrides is "one trade per day" for unsplit accounts: an
|
* The one rule it overrides is "one trade per day" for unsplit accounts: an
|
||||||
* account that already booked something today may take another leg toward
|
* account that already booked something today may take another leg toward
|
||||||
* what's left of its target. Everything else still applies — flat, history
|
* what's left of its target. Everything else still applies — flat, history
|
||||||
* loaded, the daily-loss room, the firm's trading hours, its lane flat and
|
* loaded, the daily-loss room, the firm's trading hours, no batch in flight
|
||||||
* no batch in flight there.
|
* in its lane — and a lane with positions open is only joined on the same
|
||||||
|
* instrument and side.
|
||||||
*/
|
*/
|
||||||
export async function fireManualLeg(accountId: number, action: 'Buy' | 'Sell' | 'Auto'): Promise<LegResult> {
|
export async function fireManualLeg(accountId: number, action: 'Buy' | 'Sell' | 'Auto'): Promise<LegResult> {
|
||||||
const found = firmClients().ready.find(({ client }) => client.accountList.some((a) => a.id === accountId));
|
const found = firmClients().ready.find(({ client }) => client.accountList.some((a) => a.id === accountId));
|
||||||
@@ -1398,14 +1399,28 @@ export async function fireManualLeg(accountId: number, action: 'Buy' | 'Sell' |
|
|||||||
room = gate.room;
|
room = gate.room;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A lane with positions open may only be joined on the same instrument
|
||||||
|
// and side — what a batch of like accounts would have done — never the
|
||||||
|
// other side (that is hedging).
|
||||||
const lane = laneOf(firm);
|
const lane = laneOf(firm);
|
||||||
const open = countOpenPositions(inLane(firmClients().ready, lane));
|
const held = inLane(firmClients().ready, lane).flatMap(({ client: c }) => c.accountList
|
||||||
if (open > 0) throw new Error(`${open} position(s) still open in the ${laneName(lane)} lane`);
|
.map((a) => c.positions[a.id])
|
||||||
|
.filter((p): p is NonNullable<typeof p> => !!p));
|
||||||
|
const heldSides = new Set(held.map((p) => (p.netPos > 0 ? 'Buy' : 'Sell')));
|
||||||
|
const heldContracts = new Set(held.map((p) => String(p.contractId)));
|
||||||
|
if (heldSides.size > 1 || heldContracts.size > 1) throw new Error(`the ${laneName(lane)} lane holds mixed positions — not adding to it`);
|
||||||
|
const heldSide: 'Buy' | 'Sell' | null = held.length > 0 ? (held[0].netPos > 0 ? 'Buy' : 'Sell') : null;
|
||||||
|
if (heldSide && action !== 'Auto' && action !== heldSide) {
|
||||||
|
throw new Error(`the ${laneName(lane)} lane is ${heldSide === 'Buy' ? 'long' : 'short'} — a ${action} would hedge it`);
|
||||||
|
}
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const symbol = state.symbol !== 'Auto' ? state.symbol : 'NQ';
|
const symbol = state.symbol !== 'Auto' ? state.symbol : 'NQ';
|
||||||
const resolvedAction: 'Buy' | 'Sell' = action === 'Auto' ? (Math.random() < 0.5 ? 'Buy' : 'Sell') : action;
|
const resolvedAction: 'Buy' | 'Sell' = heldSide ?? (action === 'Auto' ? (Math.random() < 0.5 ? 'Buy' : 'Sell') : action);
|
||||||
const contract = await client.findFrontMonthContract(symbol);
|
const contract = await client.findFrontMonthContract(symbol);
|
||||||
if (!contract) throw new Error(`no instrument for ${symbol}`);
|
if (!contract) throw new Error(`no instrument for ${symbol}`);
|
||||||
|
if (held.length > 0 && String(contract.id) !== [...heldContracts][0]) {
|
||||||
|
throw new Error(`the ${laneName(lane)} lane holds ${[...heldContracts][0]}, not ${contract.id} — not adding to it`);
|
||||||
|
}
|
||||||
const pv = contract.pointValue ?? POINT_VALUES[symbol];
|
const pv = contract.pointValue ?? POINT_VALUES[symbol];
|
||||||
if (!pv) throw new Error(`no point value for ${symbol}`);
|
if (!pv) throw new Error(`no point value for ${symbol}`);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user