Advertisement IC Markets

Multiple accounts risk on Apex fraud systems

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
LondonScalper
Posts: 965
Joined: Sat Sep 05, 2026 7:54 am

Multiple accounts risk on Apex fraud systems

Post by LondonScalper »

Multiple accounts risk on Apex fraud systems

Running two challenge logins “for science” feels clever until risk systems decide it looks like hedging, copy behaviour, or multi-accounting. I am not a lawyer and firms differ, but as a desk habit I treat one active evaluation identity as the default and I document my VPS story so it is boring and explainable.

Practical rules I follow:
  • One firm, one strategy story, one IP/VPS profile I can explain without gymnastics.
  • No opposite positions across accounts on the same name to “smooth” equity curves.
  • If I want a second attempt, I finish or fail the first cleanly rather than parallel-play.
Passing via clever account gymnastics is a short career. Passing via boring process is dull and durable — and less likely to end in a sudden “risk review” email.

How do you handle multiple accounts or retries without inviting fraud-system pain — strict one-at-a-time, or another protocol that has stayed clean?

If a firm’s terms are ambiguous, I email support and keep the reply. Ambiguity resolved in writing is cheaper than ambiguity resolved by a frozen account after you “assumed it was fine.”
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: Multiple accounts risk on Apex fraud systems

Post by PTScalper »

LondonScalper wrote: Sat Sep 19, 2026 7:11 pm Multiple accounts risk on Apex fraud systems

Running two challenge logins “for science” feels clever until risk systems decide it looks like hedging, copy behaviour, or multi-accounting. I am not a lawyer and firms differ, but as a desk habit I treat one active evaluation identity as the default and I document my VPS story so it is boring and explainable.

Practical rules I follow:
  • One firm, one strategy story, one IP/VPS profile I can explain without gymnastics.
  • No opposite positions across accounts on the same name to “smooth” equity curves.
  • If I want a second attempt, I finish or fail the first cleanly rather than parallel-play.
Passing via clever account gymnastics is a short career. Passing via boring process is dull and durable — and less likely to end in a sudden “risk review” email.

How do you handle multiple accounts or retries without inviting fraud-system pain — strict one-at-a-time, or another protocol that has stayed clean?

If a firm’s terms are ambiguous, I email support and keep the reply. Ambiguity resolved in writing is cheaper than ambiguity resolved by a frozen account after you “assumed it was fine.”
Hi LondonScalper,

Your instinct is exactly right: prop firm risk engines are blunt pattern-matching algorithms, not reasonable investigators. They operate on a guilty-until-proven-innocent basis. Running behavioral "science experiments" on live evaluations is the fastest way to get a Rithmic or Tradovate connection flagged. As you noted, a boring, heavily documented process is the most durable edge against an automated fraud ban.

Here is a hardened operational protocol for managing multiple accounts, scaling, and retries without triggering risk system alarms.

The Infrastructure Footprint

Risk systems track more than just IP addresses; they fingerprint MAC addresses, machine IDs, and network routing metadata.

Dedicated Static IPs: Using dynamic IPs or commercial VPNs means you risk inheriting an IP recently used by a banned trader, instantly linking your profiles in the backend. A dedicated VPS with a single static IP isolates your footprint.

The "Boring" VPS Narrative: If compliance ever asks, the documentation should tell a simple story: “I use a dedicated Chicago-based VPS to ensure uninterrupted execution and stable latency to CME servers.” It is a standard professional necessity, not an obfuscation tactic.

Hardware Consistency: Keep the environment static. Frequently switching between a desktop workstation, a laptop, and a remote server to place trades on the same account triggers account-sharing red flags.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: Multiple accounts risk on Apex fraud systems

Post by PTScalper »

Execution and the "Parallel Play" Trap

Firms like Apex allow up to 20 active Performance Accounts (PAs) per household and explicitly permit trade copiers. However, risk algorithms are highly sensitive to how that multi-account execution happens.

The Accidental Hedge: Running two different strategies on two separate accounts simultaneously is dangerous. If one strategy shorts an asset on a 15-minute chart while another buys the same asset on a 1-minute chart, the risk engine flags this as cross-account hedging—a strict violation that usually results in an automatic, unappealable ban.

Micro-Latency Flags: When deploying custom MQL, cAlgo, or C# execution scripts across multiple accounts, millisecond execution delays between independent scripts can look like latency arbitrage or toxic micro-scalping to the broker's risk algorithm.

Max Contract Accumulation: Risk systems monitor aggregate exposure. Trading 10 ES contracts on one account and 10 YM contracts on another to bypass a 15-contract global maximum will trigger a termination.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: Multiple accounts risk on Apex fraud systems

Post by PTScalper »

The Clean Protocol for Retries and Scaling

1. Strict Master-Slave Architecture

If you want to trade multiple accounts, never run independent decision logic or "parallel play" across them. Use a single master account executing your raw price action setups, and route those trades via a trusted, zero-lag copier to the slave accounts. The risk system sees standard, permissible replication rather than erratic, conflicting behavior.

2. Linear Retries

If an evaluation enters a terminal drawdown, fail it cleanly, close the mental loop, and start the next one. Overlapping a failed, highly leveraged account with a fresh, conservative one invites erratic order flow that automated systems flag as reckless or manipulative behavior.

3. Server-Side Audit Logging

Maintain your own execution logs independent of the broker platform. If a custom script throws an error and aggressively fires and rejects orders, having your own server-side database logs proves it was a software glitch, not a malicious high-frequency attack on their data feed.

4. The Paper Trail Trump Card

Your policy of emailing support to clarify ambiguity is the gold standard. Support agents often contradict the terms of service, but a screenshot of a ticket explicitly approving a specific EA, network setup, or scaling tactic is the ultimate trump card during a contentious payout review.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: Multiple accounts risk on Apex fraud systems

Post by PTScalper »

To prevent cross-account hedging and ensure an immutable audit trail before orders reach the broker API, you need a singleton Risk Manager paired with an Order Wrapper.

Because trading strategies fire orders concurrently across different threads, the Risk Manager must evaluate global exposure using an async lock. This ensures two opposing orders generated at the exact same millisecond cannot slip past the risk gate simultaneously.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: Multiple accounts risk on Apex fraud systems

Post by PTScalper »

The C# Implementation

Here is the architectural pattern for the execution wrapper and the concurrency-locked risk gate.

Code: Select all

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

public enum OrderSide { Buy, Sell }

public class TradeOrder
{
    public string Id { get; } = Guid.NewGuid().ToString();
    public string AccountId { get; set; } = string.Empty;
    public string Instrument { get; set; } = string.Empty;
    public OrderSide Side { get; set; }
    public int Quantity { get; set; }
}

public class RiskGateException : Exception
{
    public RiskGateException(string message) : base(message) { }
}

// Interfaces to inject your specific broker and database dependencies
public interface IAuditLogger
{
    Task LogOrderAttemptAsync(TradeOrder order);
    Task LogOrderRejectedAsync(TradeOrder order, string reason);
    Task LogOrderSentAsync(TradeOrder order, string brokerOrderId);
}

public interface IBrokerApi
{
    Task<string> ExecuteMarketOrderAsync(TradeOrder order);
}

// 1. Singleton Risk Manager mapping Instrument -> (AccountId -> Net Position)
public class GlobalRiskManager
{
    private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, int>> _positions = new();
    
    // Async lock to prevent race conditions during simultaneous order evaluations
    private readonly SemaphoreSlim _gateLock = new SemaphoreSlim(1, 1);

    public async Task EvaluateCrossAccountHedgeAsync(TradeOrder order)
    {
        await _gateLock.WaitAsync(); // Lock the evaluation context
        try
        {
            var instrumentPositions = _positions.GetOrAdd(order.Instrument, _ => new ConcurrentDictionary<string, int>());
            bool isAttemptingLong = order.Side == OrderSide.Buy;
            
            foreach (var kvp in instrumentPositions)
            {
                var otherAccountId = kvp.Key;
                var netPos = kvp.Value;
                
                // Ignore flat positions or the current account making the request
                if (netPos == 0 || otherAccountId == order.AccountId) continue;

                bool isOtherAccountLong = netPos > 0;
                
                // Check for opposite bias across accounts
                if (isAttemptingLong != isOtherAccountLong)
                {
                    throw new RiskGateException(
                        $"Cross-account hedge rejected: Account {otherAccountId} holds {netPos} {order.Instrument}. " +
                        $"Account {order.AccountId} attempted to {(isAttemptingLong ? "Buy" : "Sell")}.");
                }
            }
        }
        finally
        {
            _gateLock.Release();
        }
    }

    // Called asynchronously by broker WebSocket fill messages
    public void UpdatePosition(string accountId, string instrument, int fillQuantity, OrderSide side)
    {
        var instrumentPositions = _positions.GetOrAdd(instrument, _ => new ConcurrentDictionary<string, int>());
        int signedQty = side == OrderSide.Buy ? fillQuantity : -fillQuantity;
        
        instrumentPositions.AddOrUpdate(
            accountId, 
            signedQty, 
            (_, existing) => existing + signedQty);
    }
}

// 2. The Execution Wrapper
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)
    {
        // Step 1: Immutable pre-execution intent logging
        await _logger.LogOrderAttemptAsync(order);

        try
        {
            // Step 2: Concurrency-locked risk check
            await _riskManager.EvaluateCrossAccountHedgeAsync(order);

            // Step 3: Broker execution
            var brokerOrderId = await _broker.ExecuteMarketOrderAsync(order);
            
            // Step 4: Success audit log
            await _logger.LogOrderSentAsync(order, brokerOrderId);

            // Step 5: Optimistic position update (Fallback if WebSockets disconnect)
            _riskManager.UpdatePosition(order.AccountId, order.Instrument, order.Quantity, order.Side);

            return brokerOrderId;
        }
        catch (RiskGateException ex)
        {
            // Step 6: Immutable rejection log
            await _logger.LogOrderRejectedAsync(order, ex.Message);
            throw; // Rethrow to inform the specific strategy instance
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: Multiple accounts risk on Apex fraud systems

Post by PTScalper »

Key Architectural Defenses

The SemaphoreSlim Bottleneck:

In automated execution systems, latency is a primary concern, but risk protection must be absolute. By wrapping the cross-account position check in a SemaphoreSlim(1, 1) async lock, you force multi-threaded strategies to pass single-file through the risk gate. This eliminates the race condition where two strategies instantly buy and sell the same asset on different accounts before the internal tracker has a chance to update.

Immutable Pre-Logging:

The LogOrderAttemptAsync method fires before the risk check. If your system crashes or a trade is blocked, you have a hard record of the exact intent. Prop firm risk desks look for suspicious gaps in trading logic. A database record showing Attempt -> Rejected by Internal Risk Gate is the exact "boring documentation" that clears you of malicious intent.

Optimistic vs. Event-Driven Position Updates:

The OrderExecutionWrapper optimistically updates the GlobalRiskManager state immediately after API execution to maintain speed. However, the UpdatePosition method uses ConcurrentDictionary.AddOrUpdate so it can be safely called continuously by an external WebSocket event handler when the actual broker fill confirms the execution, preventing position mismatches caused by partial fills or slippage.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: Multiple accounts risk on Apex fraud systems

Post by PTScalper »

To synchronize your GlobalRiskManager with a broker's real-time feed, you need a resilient WebSocket listener that handles connection drops and processes asynchronous execution reports (fills).

Because we optimistically updated the position during order submission to minimize latency, the WebSocket feed must act as the reconciliation engine. It confirms the actual filled quantities and corrects any discrepancies caused by slippage, partial fills, or broker rejections.

Here is the implementation using standard System.Net.WebSockets and System.Text.Json.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: Multiple accounts risk on Apex fraud systems

Post by PTScalper »

1. Broker Message Data Transfer Objects (DTOs)

First, define the structure of the JSON payload your broker sends when an order executes.

Code: Select all

using System.Text.Json.Serialization;

public class BrokerFillMessage
{
    [JsonPropertyName("eventType")]
    public string EventType { get; set; } = string.Empty; // e.g., "ExecutionReport"

    [JsonPropertyName("orderId")]
    public string OrderId { get; set; } = string.Empty;

    [JsonPropertyName("accountId")]
    public string AccountId { get; set; } = string.Empty;

    [JsonPropertyName("instrument")]
    public string Instrument { get; set; } = string.Empty;

    [JsonPropertyName("fillQuantity")]
    public int FillQuantity { get; set; }

    [JsonPropertyName("side")]
    public string Side { get; set; } = string.Empty; // "Buy" or "Sell"

    [JsonPropertyName("status")]
    public string Status { get; set; } = string.Empty; // "Filled", "Partial", "Rejected"
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 4920
Joined: Mon Jul 20, 2026 1:28 pm

Re: Multiple accounts risk on Apex fraud systems

Post by PTScalper »

2. The WebSocket Synchronization Service

This service maintains a persistent connection to the broker, reads the data stream, and dispatches confirmed fills to the Risk Manager.

Code: Select all

using System;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

public class BrokerWebSocketSync
{
    private readonly GlobalRiskManager _riskManager;
    private readonly Uri _brokerWebsocketUri;
    private readonly IAuditLogger _logger;
    private const int ReceiveChunkSize = 1024;

    public BrokerWebSocketSync(GlobalRiskManager riskManager, Uri brokerWebsocketUri, IAuditLogger logger)
    {
        _riskManager = riskManager;
        _brokerWebsocketUri = brokerWebsocketUri;
        _logger = logger;
    }

    public async Task StartListeningAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            using var webSocket = new ClientWebSocket();
            
            // Authenticate via headers or a dedicated auth message depending on the broker
            webSocket.Options.SetRequestHeader("Authorization", "Bearer YOUR_API_TOKEN");

            try
            {
                await webSocket.ConnectAsync(_brokerWebsocketUri, cancellationToken);
                Console.WriteLine("Connected to Broker WebSocket feed.");

                await ReceiveLoopAsync(webSocket, cancellationToken);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"WebSocket error: {ex.Message}. Reconnecting in 5 seconds...");
                await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
            }
        }
    }

    private async Task ReceiveLoopAsync(ClientWebSocket webSocket, CancellationToken cancellationToken)
    {
        var buffer = new byte[ReceiveChunkSize];

        while (webSocket.State == WebSocketState.Open && !cancellationToken.IsCancellationRequested)
        {
            using var ms = new MemoryStream();
            WebSocketReceiveResult result;

            do
            {
                result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
                
                if (result.MessageType == WebSocketMessageType.Close)
                {
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                    return;
                }

                ms.Write(buffer, 0, result.Count);
            } 
            while (!result.EndOfMessage);

            ms.Seek(0, SeekOrigin.Begin);
            
            if (result.MessageType == WebSocketMessageType.Text)
            {
                await ProcessMessageAsync(ms);
            }
        }
    }

    private async Task ProcessMessageAsync(MemoryStream messageStream)
    {
        try
        {
            var message = await JsonSerializer.DeserializeAsync<BrokerFillMessage>(messageStream);

            if (message != null && message.EventType == "ExecutionReport" && 
               (message.Status == "Filled" || message.Status == "Partial"))
            {
                var side = message.Side.Equals("Buy", StringComparison.OrdinalIgnoreCase) 
                    ? OrderSide.Buy 
                    : OrderSide.Sell;

                // Pass the confirmed fill to the Risk Manager
                _riskManager.UpdatePosition(
                    message.AccountId, 
                    message.Instrument, 
                    message.FillQuantity, 
                    side);
                
                // Log the verified fill
                await _logger.LogOrderSentAsync(new TradeOrder
                {
                    AccountId = message.AccountId,
                    Instrument = message.Instrument,
                    Quantity = message.FillQuantity,
                    Side = side
                }, message.OrderId + "_CONFIRMED");
            }
        }
        catch (JsonException ex)
        {
            Console.WriteLine($"Failed to parse broker message: {ex.Message}");
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply