OrderRegistry
Central registry for all orders and families. This is the single source of truth for order state. All lookups and updates go through this registry. Thread-safe through ConcurrentHashMap and careful synchronization.
OrderRegistry.java
package com.bookmap.ordermanagement.core;
import com.bookmap.ordermanagement.model.*;
import velox.api.layer1.common.Log;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* Central registry for all orders and families.
*
* This is the single source of truth for order state.
* All lookups and updates go through this registry.
*
* Thread-safe through ConcurrentHashMap and careful synchronization.
*/
public class OrderRegistry {
// Configuration
private final String logPrefix;
private final boolean loggingEnabled;
// Data storage
private final Map<String, OrderRecord> ordersByBrokerId;
private final Map<String, OrderFamily> familiesById;
// Active family tracking
private volatile String activeFamilyId;
// ID generation
private final AtomicLong familyCounter;
/**
* Creates a new order registry.
*
* @param logPrefix Prefix for log messages
* @param loggingEnabled Whether to enable detailed logging
*/
public OrderRegistry(String logPrefix, boolean loggingEnabled) {
this.logPrefix = logPrefix;
this.loggingEnabled = loggingEnabled;
this.ordersByBrokerId = new ConcurrentHashMap<>();
this.familiesById = new ConcurrentHashMap<>();
this.activeFamilyId = null;
this.familyCounter = new AtomicLong(System.currentTimeMillis());
}
// =========================================================================
// Family Management
// =========================================================================
/**
* Creates a new order family.
*
* @param direction Entry direction (BUY for long, SELL for short)
* @param size Number of contracts
* @return The created family
*/
public OrderFamily createFamily(Direction direction, int size) {
String familyId = "FAM_" + familyCounter.incrementAndGet();
OrderFamily family = new OrderFamily(familyId, direction, size);
familiesById.put(familyId, family);
activeFamilyId = familyId;
if (loggingEnabled) {
Log.info(String.format("%s Created family: %s, direction=%s, size=%d",
logPrefix, familyId, direction, size));
}
return family;
}
/**
* Gets a family by ID.
*/
public OrderFamily getFamily(String familyId) {
return familiesById.get(familyId);
}
/**
* Gets the currently active family, or null if none.
*/
public OrderFamily getActiveFamily() {
if (activeFamilyId == null) return null;
return familiesById.get(activeFamilyId);
}
/**
* Gets the active family ID, or null if none.
*/
public String getActiveFamilyId() {
return activeFamilyId;
}
/**
* Clears the active family reference.
*/
public void clearActiveFamily() {
if (loggingEnabled && activeFamilyId != null) {
Log.info(String.format("%s Cleared active family: %s", logPrefix, activeFamilyId));
}
this.activeFamilyId = null;
}
// =========================================================================
// Order Management
// =========================================================================
/**
* Registers a new order with its broker ID.
*
* @param brokerId The broker-assigned order ID
* @param familyId The family this order belongs to
* @param orderType ENTRY, TAKE_PROFIT, or STOP_LOSS
* @param direction BUY or SELL
* @param size Requested size
* @return The created order record
*/
public OrderRecord registerOrder(String brokerId, String familyId,
OrderType orderType, Direction direction, int size) {
OrderRecord record = new OrderRecord(brokerId, familyId, orderType, direction, size);
ordersByBrokerId.put(brokerId, record);
// Link to family
OrderFamily family = familiesById.get(familyId);
if (family != null) {
switch (orderType) {
case ENTRY:
family.setEntryBrokerId(brokerId);
break;
case TAKE_PROFIT:
family.setTpBrokerId(brokerId);
break;
case STOP_LOSS:
family.setSlBrokerId(brokerId);
break;
}
}
if (loggingEnabled) {
Log.info(String.format("%s Registered order: %s, type=%s, family=%s",
logPrefix, brokerId, orderType, familyId));
}
return record;
}
/**
* Finds an order by broker ID.
*/
public OrderRecord findByBrokerId(String brokerId) {
return ordersByBrokerId.get(brokerId);
}
/**
* Updates an order's state.
*
* @param brokerId The order's broker ID
* @param newState The new state
* @return true if order was found and updated
*/
public boolean updateOrderState(String brokerId, OrderState newState) {
OrderRecord record = ordersByBrokerId.get(brokerId);
if (record == null) {
return false;
}
OrderState oldState = record.getState();
record.setState(newState);
if (loggingEnabled) {
Log.info(String.format("%s Order state change: %s [%s] %s -> %s",
logPrefix, brokerId, record.getOrderType(), oldState, newState));
}
return true;
}
/**
* Records a fill on an order.
*
* @param brokerId The order's broker ID
* @param quantity Fill quantity
* @param price Fill price
* @return The updated order record, or null if not found
*/
public OrderRecord recordFill(String brokerId, int quantity, double price) {
OrderRecord record = ordersByBrokerId.get(brokerId);
if (record == null) {
Log.warn(String.format("%s Fill for unknown order: %s", logPrefix, brokerId));
return null;
}
record.addFill(quantity, price);
if (loggingEnabled) {
Log.info(String.format("%s Fill recorded: %s, qty=%d, price=%.2f, total=%d/%d",
logPrefix, brokerId, quantity, price,
record.getFilledQuantity(), record.getRequestedSize()));
}
return record;
}
// =========================================================================
// Family State Queries
// =========================================================================
/**
* Gets all orders belonging to a family.
*/
public List<OrderRecord> getFamilyOrders(String familyId) {
List<OrderRecord> orders = new ArrayList<>();
OrderFamily family = familiesById.get(familyId);
if (family == null) return orders;
for (String brokerId : family.getAllBrokerIds()) {
OrderRecord record = ordersByBrokerId.get(brokerId);
if (record != null) {
orders.add(record);
}
}
return orders;
}
/**
* Checks if a family is complete.
*
* A family is complete when:
* - Entry was REJECTED (family failed before TP/SL placed), OR
* - An EXIT order (TP or SL) has been FILLED
*
* IMPORTANT: Entry being FILLED or CANCELLED alone does NOT make the family complete.
* We must wait for an exit order to fill (or entry to be rejected).
*/
public boolean isFamilyComplete(String familyId) {
OrderFamily family = familiesById.get(familyId);
if (family == null) {
if (loggingEnabled) {
Log.info(String.format("%s isFamilyComplete(%s): false - family not found", logPrefix, familyId));
}
return false;
}
// Check if entry was rejected (family failed before TP/SL)
String entryId = family.getEntryBrokerId();
if (entryId != null) {
OrderRecord entry = ordersByBrokerId.get(entryId);
if (entry != null && entry.getState() == OrderState.REJECTED) {
if (loggingEnabled) {
Log.info(String.format("%s isFamilyComplete(%s): true - entry REJECTED", logPrefix, familyId));
}
return true;
}
}
// Family is complete only when an EXIT order filled
String tpId = family.getTpBrokerId();
String slId = family.getSlBrokerId();
if (tpId != null) {
OrderRecord tp = ordersByBrokerId.get(tpId);
if (tp != null && tp.isFilled()) {
if (loggingEnabled) {
Log.info(String.format("%s isFamilyComplete(%s): true - TP filled", logPrefix, familyId));
}
return true;
}
}
if (slId != null) {
OrderRecord sl = ordersByBrokerId.get(slId);
if (sl != null && sl.isFilled()) {
if (loggingEnabled) {
Log.info(String.format("%s isFamilyComplete(%s): true - SL filled", logPrefix, familyId));
}
return true;
}
}
if (loggingEnabled) {
Log.info(String.format("%s isFamilyComplete(%s): false - waiting for exit fill (TP=%s, SL=%s)",
logPrefix, familyId,
tpId != null ? "registered" : "pending",
slId != null ? "registered" : "pending"));
}
return false;
}
/**
* Determines the close reason for a completed family.
*/
public CloseReason determineFamilyCloseReason(String familyId) {
OrderFamily family = familiesById.get(familyId);
if (family == null) return CloseReason.UNKNOWN;
// Check TP
String tpId = family.getTpBrokerId();
if (tpId != null) {
OrderRecord tpOrder = ordersByBrokerId.get(tpId);
if (tpOrder != null && tpOrder.isFilled()) {
return CloseReason.TP_HIT;
}
}
// Check SL
String slId = family.getSlBrokerId();
if (slId != null) {
OrderRecord slOrder = ordersByBrokerId.get(slId);
if (slOrder != null && slOrder.isFilled()) {
return CloseReason.SL_HIT;
}
}
// Check for rejection
String entryId = family.getEntryBrokerId();
if (entryId != null) {
OrderRecord entryOrder = ordersByBrokerId.get(entryId);
if (entryOrder != null && entryOrder.getState() == OrderState.REJECTED) {
return CloseReason.REJECTED;
}
}
// Must be manual or cancelled
return CloseReason.CANCELLED;
}
// =========================================================================
// Global Queries
// =========================================================================
/**
* Returns true if there are any active (non-terminal) orders.
*/
public boolean hasActiveOrders() {
for (OrderRecord record : ordersByBrokerId.values()) {
if (record.isActive()) {
return true;
}
}
return false;
}
/**
* Returns the count of active orders.
*/
public int getActiveOrderCount() {
int count = 0;
for (OrderRecord record : ordersByBrokerId.values()) {
if (record.isActive()) {
count++;
}
}
return count;
}
/**
* Returns the count of all tracked orders.
*/
public int getTotalOrderCount() {
return ordersByBrokerId.size();
}
/**
* Returns the count of all families.
*/
public int getTotalFamilyCount() {
return familiesById.size();
}
// =========================================================================
// Reset and Cleanup
// =========================================================================
/**
* Clears all state.
*/
public void clear() {
ordersByBrokerId.clear();
familiesById.clear();
activeFamilyId = null;
if (loggingEnabled) {
Log.info(String.format("%s Registry cleared", logPrefix));
}
}
/**
* Removes completed families older than the specified age.
*
* @param maxAgeMs Maximum age in milliseconds
* @return Number of families removed
*/
public int cleanupOldFamilies(long maxAgeMs) {
long cutoff = System.currentTimeMillis() - maxAgeMs;
int removed = 0;
Iterator<Map.Entry<String, OrderFamily>> it = familiesById.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, OrderFamily> entry = it.next();
OrderFamily family = entry.getValue();
if (family.isClosed() && family.getClosedTime() < cutoff) {
// Remove associated orders
for (String brokerId : family.getAllBrokerIds()) {
ordersByBrokerId.remove(brokerId);
}
it.remove();
removed++;
}
}
if (loggingEnabled && removed > 0) {
Log.info(String.format("%s Cleaned up %d old families", logPrefix, removed));
}
return removed;
}
}