Project: A Multi-Symbol DAS Observation Notebook
Combine collections, quote objects and validated observation records.
Project specification
Prerequisites: collections, quote state and custom objects. Build a manual snapshot notebook for two preloaded symbols. Each output row contains symbol, copied bid/ask, spread, script observation time and validity. It does not rank opportunities or create orders. The notebook owns its own collection and does not overwrite QuoteObj.data.
Setup and input contract
Open quotes for MSFT and AAPL in a dedicated practice layout. Verify GetQuoteObj for each independently before running the collection. The code validates positive, ordered prices but does not handle an unavailable object using an invented null idiom. A quote can be numerically plausible yet stale; validity here means only that the specified numeric checks passed.
Complete implementation
$feSymbols = NewUserObj();
$feSymbols.Set(0, "MSFT");
$feSymbols.Set(1, "AAPL");
$feSymbolCount = 2;
$feNotebook = NewUserObj();
$feNotebook.schema = 1;
$feNotebook.count = $feSymbolCount;
$feNotebook.rows = NewUserObj();
$feNotebook.validCount = 0;
$feNotebook.complete = 0;
$feRowIndex = 0;
while ($feRowIndex < $feSymbolCount) {
$feSymbol = $feSymbols.Get($feRowIndex);
$feQuoteForRow = GetQuoteObj($feSymbol);
$feRow = NewUserObj();
$feRow.symbol = $feSymbol;
$feRow.second = GetSecond();
$feRow.bid = $feQuoteForRow.BID;
$feRow.ask = $feQuoteForRow.ASK;
$feRow.spread = 0;
$feRow.valid = 0;
if ($feRow.bid > 0) {
if ($feRow.ask >= $feRow.bid) {
$feRow.spread = $feRow.ask - $feRow.bid;
$feRow.valid = 1;
$feNotebook.validCount = $feNotebook.validCount + 1;
}
}
$feNotebook.rows.Set($feRowIndex, $feRow);
$feRowIndex = $feRowIndex + 1;
}
$feNotebook.complete = 1;
$feNotebook.ShowObject();
Follow the data flow
Configuration is built first. A new notebook starts incomplete. Each iteration allocates a separate row, so it does not deliberately reuse one mutable row for every key. The copied prices are validated and only then contribute to validCount. Every requested symbol still gets a row even when its numeric checks fail. complete is set only after the loop finishes; it does not mean all rows are valid.
Acceptance checks
Compare every row with its visible quote and record the comparison time. Expect count=2 and complete=1; validCount may be 0,1 or 2. Verify two different symbols have separate rows. Re-run manually after changing one configured symbol and confirm no old rows survive in the fresh notebook. Do not call this an atomic two-symbol snapshot: reads happen sequentially and can span different market updates.
Extension exercise and worked answer
Add a report listing only invalid rows. Answer: iterate notebook.rows from 0 to count-1, inspect row.valid and append that row’s symbol to a display string only when valid=0. Keep the original notebook unchanged. Before making the observer periodic, split initialization from sampling and add enable/stop controls as in the Timer chapter; never recreate user configuration on every tick.