SymbolContext¶
SymbolContext aggregates all per-symbol state needed by trading strategies: order book, position, prices, and timestamps.
struct SymbolContext
{
static constexpr size_t kDefaultBookLevels = 512;
NLevelOrderBook<kDefaultBookLevels> book;
Quantity position{};
std::optional<Price> avgEntryPrice{};
Price lastTradePrice{};
int64_t lastUpdateNs{0};
SymbolId symbolId{0};
};
Purpose¶
- Consolidate all per-symbol data in a cache-friendly structure
- Provide computed properties (mid price, spread, PnL)
- Support multi-symbol strategies with O(1) state access
Fields¶
| Field | Type | Description |
|---|---|---|
book |
NLevelOrderBook<512> |
Order book with 512 price levels |
position |
Quantity |
Net position (positive=long, negative=short) |
avgEntryPrice |
std::optional<Price> |
Volume-weighted average entry price, empty when the attached position manager reports none |
lastTradePrice |
Price |
Most recent trade price |
lastUpdateNs |
int64_t |
Last update timestamp (nanoseconds) |
symbolId |
SymbolId |
Symbol identifier |
Computed Properties¶
Mid Price¶
Returns midpoint between best bid and ask. Returns nullopt if either side is missing.
Book Spread¶
Returns bid-ask spread. Returns nullopt if book is one-sided.
Unrealized PnL¶
std::optional<double> unrealizedPnl(Price markPrice) const noexcept;
std::optional<double> unrealizedPnl() const noexcept; // Uses mid() as mark
Unrealized PnL against the average entry price, which the engine reads from the
attached IPositionManager before every handler call. Empty when there is no
entry price to measure against, either because the manager keeps no cost basis
or because the no-argument form has no mid to mark at. A flat position returns
0.0, not an empty optional.
Substituting zero for a missing entry price would report position * mark, the
whole notional of the position dressed up as profit. That is what this returned
before IPositionManager could be asked for one, and it meant a rule like
"close when the loss passes X" never fired on a long and fired on the first
tick of a short.
Position State¶
bool isLong() const noexcept; // position > 0
bool isShort() const noexcept; // position < 0
bool isFlat() const noexcept; // position == 0
Reset¶
Clears all state: book, position, prices, timestamps.
Cross-Symbol Helpers¶
Free functions for multi-symbol analysis:
// Price spread: midA - midB
std::optional<Price> spread(const SymbolContext& a, const SymbolContext& b);
// Price ratio: midA / midB
std::optional<double> ratio(const SymbolContext& a, const SymbolContext& b);
Example¶
class SpreadStrategy : public Strategy
{
public:
SpreadStrategy(SymbolId front, SymbolId back, const SymbolRegistry& registry)
: Strategy(1, {front, back}, registry), _front(front), _back(back) {}
protected:
void onSymbolBook(SymbolContext& c, const BookUpdateEvent& ev) override
{
// Get spread between front and back month
auto spreadOpt = spread(ctx(_front), ctx(_back));
if (!spreadOpt) return;
double spreadValue = spreadOpt->toDouble();
// Check unrealized PnL. Either leg can report "unknown".
auto frontPnl = ctx(_front).unrealizedPnl();
auto backPnl = ctx(_back).unrealizedPnl();
if (!frontPnl || !backPnl) return;
double pnl = *frontPnl + *backPnl;
// Check position state
if (ctx(_front).isLong() && pnl > _target)
{
emitClosePosition(_front);
emitClosePosition(_back);
}
}
private:
SymbolId _front, _back;
double _target{100.0};
};
Memory Layout¶
SymbolContext is designed for cache efficiency:
- 8,384 bytes per symbol in a release build; the 512-level book dominates at
8,320 of them. A build with
LRVX_SCALE_CHECKSon (any build withoutNDEBUG) carries a scale field on everyDecimaland doubles this to 16,640 Strategyholds 256 of these by value throughSymbolStateMap, so the strategy object is about 2 MB in release and about 4 MB in a checked build. Allocate a strategy on the heap: two on one stack frame overrun a default 8 MB stack in a checked build, and the overflow lands in the constructor prologue- All fields in single contiguous struct
- Access via
SymbolStateMapprovides O(1) lookup
See Also¶
- Strategy - Strategy base class
- NLevelOrderBook - Order book implementation
- SymbolStateMap - O(1) container for per-symbol state