Dealing with the "Double Counting" Problem
Because your wrapper optimistically assumes the trade filled at the exact quantity requested, a naive WebSocket integration will double-count positions (once upon order submission, once upon receiving the WebSocket fill).
To fix this without sacrificing latency, modify the GlobalRiskManager to track Pending and Confirmed quantities separately:
Submission Phase: When SubmitOrderAsync fires, it adds to the PendingQuantity.
Reconciliation Phase: When the WebSocket receives the ExecutionReport, the GlobalRiskManager subtracts from PendingQuantity and adds the exact amount to ConfirmedQuantity.
Risk Check Adjustment: The EvaluateCrossAccountHedgeAsync method then calculates total exposure as (PendingQuantity + ConfirmedQuantity) before allowing the next trade.
Multiple accounts risk on Apex fraud systems
Re: Multiple accounts risk on Apex fraud systems
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Multiple accounts risk on Apex fraud systems
To solve the double-counting problem, we need to introduce a PositionState class that uses System.Threading.Interlocked to atomically update values without locking the entire dictionary.
The strategy shifts from a simple integer to a dual-state model:
Pending: Registered instantly inside the risk check lock.
Confirmed: Updated asynchronously by the WebSocket, releasing the pending amount.
Here is the updated implementation.
The strategy shifts from a simple integer to a dual-state model:
Pending: Registered instantly inside the risk check lock.
Confirmed: Updated asynchronously by the WebSocket, releasing the pending amount.
Here is the updated implementation.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Multiple accounts risk on Apex fraud systems
1. The Atomic Position State
This class tracks the net exposure securely across multiple threads.
This class tracks the net exposure securely across multiple threads.
Code: Select all
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
public class PositionState
{
private int _pendingQuantity;
private int _confirmedQuantity;
public int PendingQuantity => _pendingQuantity;
public int ConfirmedQuantity => _confirmedQuantity;
// Total exposure is what the risk gate evaluates
public int NetExposure => _pendingQuantity + _confirmedQuantity;
public void AddPending(int amount)
{
Interlocked.Add(ref _pendingQuantity, amount);
}
public void ConfirmFill(int pendingToRelease, int confirmedAmount)
{
// Remove from pending, add to confirmed atomically
Interlocked.Add(ref _pendingQuantity, -pendingToRelease);
Interlocked.Add(ref _confirmedQuantity, confirmedAmount);
}
public void RevertPending(int amount)
{
Interlocked.Add(ref _pendingQuantity, -amount);
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Multiple accounts risk on Apex fraud systems
2. The Updated Risk Manager
The GlobalRiskManager now registers the pending quantity inside the SemaphoreSlim lock. This is critical: if you check the risk and then release the lock before adding the pending quantity, a concurrent thread could slip an opposing order through the gap.
The GlobalRiskManager now registers the pending quantity inside the SemaphoreSlim lock. This is critical: if you check the risk and then release the lock before adding the pending quantity, a concurrent thread could slip an opposing order through the gap.
Code: Select all
public class GlobalRiskManager
{
// Dictionary mapping Instrument -> (AccountId -> PositionState)
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, PositionState>> _positions = new();
private readonly SemaphoreSlim _gateLock = new SemaphoreSlim(1, 1);
private PositionState GetOrAddPositionState(string instrument, string accountId)
{
var accountPositions = _positions.GetOrAdd(instrument, _ => new ConcurrentDictionary<string, PositionState>());
return accountPositions.GetOrAdd(accountId, _ => new PositionState());
}
public async Task EvaluateAndRegisterOrderAsync(TradeOrder order)
{
await _gateLock.WaitAsync();
try
{
var instrumentPositions = _positions.GetOrAdd(order.Instrument, _ => new ConcurrentDictionary<string, PositionState>());
bool isAttemptingLong = order.Side == OrderSide.Buy;
foreach (var kvp in instrumentPositions)
{
var otherAccountId = kvp.Key;
var state = kvp.Value;
var netPos = state.NetExposure; // Uses Pending + Confirmed
if (netPos == 0 || otherAccountId == order.AccountId) continue;
bool isOtherAccountLong = netPos > 0;
if (isAttemptingLong != isOtherAccountLong)
{
throw new RiskGateException(
$"Cross-account hedge rejected: Account {otherAccountId} holds {netPos} net {order.Instrument}. " +
$"Account {order.AccountId} attempted to {(isAttemptingLong ? "Buy" : "Sell")}.");
}
}
// Risk passed. Optimistically register the pending order IMMEDIATELY
// inside the lock to block concurrent opposite orders.
int signedQty = isAttemptingLong ? order.Quantity : -order.Quantity;
GetOrAddPositionState(order.Instrument, order.AccountId).AddPending(signedQty);
}
finally
{
_gateLock.Release();
}
}
// Called by the WebSocket service when an ExecutionReport arrives
public void ConfirmWebSocketFill(string accountId, string instrument, int requestedQty, int filledQty, OrderSide side)
{
var state = GetOrAddPositionState(instrument, accountId);
int signedRequested = side == OrderSide.Buy ? requestedQty : -requestedQty;
int signedFilled = side == OrderSide.Buy ? filledQty : -filledQty;
state.ConfirmFill(signedRequested, signedFilled);
}
// Called by the Wrapper if the synchronous REST API call fails
public void RevertFailedOrder(string accountId, string instrument, int requestedQty, OrderSide side)
{
var state = GetOrAddPositionState(instrument, accountId);
int signedQty = side == OrderSide.Buy ? requestedQty : -requestedQty;
state.RevertPending(signedQty);
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Multiple accounts risk on Apex fraud systems
3. The Refactored Execution Wrapper
The execution wrapper must now handle scenarios where the order passes the internal risk check but is rejected by the broker API (e.g., insufficient margin or network timeout). In these cases, it must revert the pending quantity so the system doesn't permanently lock up "ghost" exposure.
The execution wrapper must now handle scenarios where the order passes the internal risk check but is rejected by the broker API (e.g., insufficient margin or network timeout). In these cases, it must revert the pending quantity so the system doesn't permanently lock up "ghost" exposure.
Code: Select all
public class OrderExecutionWrapper
{
private readonly GlobalRiskManager _riskManager;
private readonly IBrokerApi _broker;
private readonly IAuditLogger _logger;
public OrderExecutionWrapper(GlobalRiskManager riskManager, IBrokerApi broker, IAuditLogger logger)
{
_riskManager = riskManager;
_broker = broker;
_logger = logger;
}
public async Task<string> SubmitOrderAsync(TradeOrder order)
{
await _logger.LogOrderAttemptAsync(order);
// Step 1: Lock, check risk, and register pending quantity
await _riskManager.EvaluateAndRegisterOrderAsync(order);
try
{
// Step 2: Fire to broker
var brokerOrderId = await _broker.ExecuteMarketOrderAsync(order);
await _logger.LogOrderSentAsync(order, brokerOrderId);
return brokerOrderId;
// We DO NOT update position here anymore.
// The WebSocket handles the confirmation and releases the pending state.
}
catch (Exception ex)
{
// Step 3: If API submission fails, revert the pending state immediately
_riskManager.RevertFailedOrder(order.AccountId, order.Instrument, order.Quantity, order.Side);
await _logger.LogOrderRejectedAsync(order, ex.Message);
throw;
}
}
}Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Re: Multiple accounts risk on Apex fraud systems
This architecture ensures your net exposure calculation is always ahead of real-time trading flow while remaining perfectly self-correcting via the WebSocket reconciliation loop.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
-
LondonNewsTrader
- Posts: 421
- Joined: Mon Sep 21, 2026 9:30 am
Re: Multiple accounts risk on Apex fraud systems
That works inside one process, but SemaphoreSlim does nothing across processes or machines. If each account runs in its own instance, or the strategies live on two VPSs, each has its own semaphore and the race condition you describe is back. For that case the gate needs a shared store with an atomic operation, a database row lock or a Redis key with a short expiry, for example.
The release must also sit in a finally block. If the risk check throws, a semaphore that is never released silently blocks every later order, which on a release day looks like the platform has frozen.
The optimistic update needs a rollback path too. If the broker rejects the order or fills it partially, GlobalRiskManager holds a position that doesn't exist until the WebSocket event corrects it, and anything that checks exposure in between is working from wrong numbers.
The pre-logging idea is good. A clean record of intent is exactly what helps in any dispute.
-
LondonScalper
- Posts: 965
- Joined: Sat Sep 05, 2026 7:54 am
Re: Multiple accounts risk on Apex fraud systems
Guilty-until-proven-innocent is the right model for those risk engines. Clever dual-login experiments read as hedging or copy behaviour long before anyone reads your notebook.
My own line stays dull on purpose: one evaluation story at a time, same device fingerprint, written notes on why size and timing look the way they do. If two accounts ever share a roof, they do not share entries, and I assume a human will never “understand the science.”
The durable edge against an automated ban is looking boring. Anything that looks like a lab test is bait.
My own line stays dull on purpose: one evaluation story at a time, same device fingerprint, written notes on why size and timing look the way they do. If two accounts ever share a roof, they do not share entries, and I assume a human will never “understand the science.”
The durable edge against an automated ban is looking boring. Anything that looks like a lab test is bait.
-
FXS Prop Desk
- Posts: 649
- Joined: Wed Oct 07, 2026 9:52 am
Re: Multiple accounts risk on Apex fraud systems
Anyone trading multiple prop accounts should check the specific firm's rules first. Many firms limit the number of accounts per person, prohibit copy trading between accounts, or treat hedging across accounts as a violation, and the consequence can be loss of all accounts and payouts. Reading the terms and asking support in writing before setting up a copier is the safest approach.
A number of firms permit copying only between accounts of the same person at that firm, while banning it across firms. The wording differs a lot, so the exact clause matters more than general advice.
A number of firms permit copying only between accounts of the same person at that firm, while banning it across firms. The wording differs a lot, so the exact clause matters more than general advice.
FXS Official Team | The Idea Lab & Strategy