Overview
Built for quantitative finance internship preparation, this card-based trading simulator implements real-time order matching, custom contract creation, and Monte Carlo AI strategies. Players trade futures contracts on card properties, with information revealing progressively through the game—combining expected value calculations with market microstructure analysis.

System Architecture
Electron Multi-Process Design
The application leverages Electron's architecture to separate concerns. The main process handles game state, AI execution, and order matching, while the renderer process manages UI and visualization. Secure IPC channels enable real-time market updates without blocking either process.
Game Phases

The game progresses through four phases:
- Configuration: Select active contracts and AI opponent strategies
- Preparation: Analyze initial card information and contract values
- Live Trading: Real-time order placement with continuous price discovery as cards reveal
- Settlement: Calculate final contract values and P&L across all positions
Core Implementation Systems
Real-Time Order Matching Engine
The matching engine implements price-time priority: best prices execute first, then by arrival time at each level. The order book maintains separate buy/sell queues sorted by price and timestamp.
function matchOrder(newOrder):
opposingOrders = getOpposingOrders(newOrder)
sortedOrders = sortByPriceTimePriority(opposingOrders)
for each existingOrder in sortedOrders:
if newOrder.volume <= 0: break
tradeVolume = min(newOrder.volume, existingOrder.volume)
executeTrade(newOrder, existingOrder, tradeVolume)
updateOrderStatus(existingOrder, tradeVolume)
newOrder.volume -= tradeVolume
broadcastMarketUpdate()Large orders execute via partial fills across multiple counterparties at different price levels. The system supports market orders (immediate execution) and limit orders (rest in book until matched). IPC broadcasts market updates to all clients immediately, creating realistic bid-ask spreads through natural price-time priority dynamics.
Visual Contract Definition System
A node-based editor enables custom financial instruments via directed acyclic graphs. Players chain operations (data sources → filters → arithmetic → aggregations) to define contract payoffs without hard-coding.

Example Pipeline: "Sum of black community number cards"
- Source: Community cards
- Filter: Black cards
- Filter: Number cards (exclude face)
- Aggregate: Sum values
The evaluation engine performs topological sorting to determine execution order and validates dependencies before contract creation. Sequential evaluation maintains intermediate results, supporting branching logic for complex multi-operation contracts.
Monte Carlo AI Strategy
The AI uses pluggable strategies for market analysis. The Monte Carlo implementation estimates fair value through simulation, operating as an "anytime" algorithm where confidence improves with successive batches across trading turns.
The simulation identifies unknown cards not yet revealed, then generates thousands of scenarios by randomly distributing these unknowns. For each scenario, it calculates contract values and maintains a running average (fair value estimate) and variance (confidence measure). Higher confidence drives more aggressive trading behavior.
function estimateFairValue(gameState, contractId):
knownCards = getVisibleCards(gameState)
unknownCards = createDeck() - knownCards
totalValue = 0
simulationCount = 0
for i = 1 to SIMULATIONS_PER_BATCH:
shuffledUnknown = shuffle(unknownCards)
simulatedState = distributeCards(gameState, shuffledUnknown)
contractValue = evaluateContract(contractId, simulatedState)
totalValue += contractValue
simulationCount++
fairValueEstimate = totalValue / simulationCount
confidence = calculateConfidence(simulationResults)
return {fairValueEstimate, confidence}Challenges
Opponent Modeling
Monte Carlo simulation estimates contract values but misses a critical element: modeling adaptive opponents. The difficultly with creating a opponent model is that it is quite subjective and intuitive. It's a mix of logic and heuristics. Even with good microstructure, there is little to be gained playing against a opponent that cannot model your actions.
My proposed approach was quite Bayesian. For each agent, the goal is to build a probability distribution over the other agent's possible cards. Using this, when running a MC simulation, instead of using a uniform distribtion, we sample using the predicted probabilities, known as importance sampling. I think a Bayes net would be a solid architecture, with trades/initations as the observed variables. Even with this, there are a lot of varaibles to represent and the parameters might be hard to estimate.
However, this approach is quite computationally expensive and I did not have time to implement it. In the future, I would like to test this and see if it is sensible. Even still, I don't believe it will able to compete even close to a human trader, making it not very useful for practice.
Execution
The other issue was how to execute the orders. The Monte Carlo simulation provides a strong estimate of the fair value, but then the bot needs to execute the order. I implemented a few strategies for this, but this is bot dependent and does not have a single answer. On the bright side, this allows for diversity in the bots' behavior, and certainly has a lot of room for future improvement with interesting approaches.
Conclusion
Ultimately, while I don't believe this project helped improve my trading abilities, mainly due to the inability to model adaptive opponents, it was a valuable learning experience. I gained a good understanding of how markets function and how to build a trading system.