Schedule a stock screen with data refresh and validation
A scheduled stock screen is reliable only when the entire dependency chain succeeds for the intended data date. Define the market session, refresh the input universe, validate the required fields, execute the rules, compare the new list with the previous accepted list, and publish a dated result. A timer firing is evidence of a trigger, not evidence that current data was retrieved or the selection was completed.
This tutorial implements that sequence in a small offline Python object. The scheduler flag defaults to false, no operating-system job is registered, and publication means an in-memory result only. You manually call a dry-run function with synthetic inputs. It does not enable Vibe scheduling, invoke an AI model, send a notification, place an order or establish that CoTrading implements a scheduled screening service.
Write a schedule contract with a data date
Specify the universe, rule version, expected data date, market timezone, permitted trigger window and approved calendar source. Keep the intended session date separate from the instant at which a delayed trigger arrives. A run for the October 20 session might be invoked at 16:30 and retried at 17:00; both belong to the same logical slot. Treating each timestamp as a new job would allow repeated outputs for one requested update.
The fixture uses a fixed UTC+08 offset and an eligible time of 16:00. Its explicit synthetic calendar contains October 19, 20 and 21, 2026; October 24 is closed. This calendar demonstrates branching, not the completeness of an exchange holiday feed. Python datetime documentation explains aware timestamps used by the script. The example rejects an absent timezone or a different offset rather than silently converting a local clock.
For real operation, obtain an authoritative market calendar and define early closes, special sessions and data-vendor delivery delays. The eligibility time should allow the fields required by your rule to become available. Merely scheduling something after an exchange’s closing time does not guarantee that a final financial dataset has arrived. Those are input availability conditions that must be checked during the run.
Make refresh a prerequisite, not a suggestion
The example’s refresh method records an actual function call and deep-copies the supplied provider payload. The payload is an offline substitute for a data adapter. A successful real adapter would need to return all required securities, field dates and provenance. An exception should stop the current run and retain an error reason; yesterday’s stored list must not be passed off as today’s newly calculated result.
The test universe is exactly AAA and BBB. Validation requires both symbols, a date matching the intended session, and finite positive PE values. Decimal’s official documentation describes numeric special values, so the program explicitly rejects NaN and infinities after parsing. Missing values, stale dates and nonpositive PE are different eligibility failures, even if several ultimately block the same current output.
These checks are intentionally specific. A broader screen might use forward EPS, liquidity or industry exclusions, each with its own date and unit contract. Add those requirements before expanding the universe. Do not let an adapter return a smaller universe silently and then announce a successful empty shortlist. A dataset containing only the easy-to-fetch companies answers a different screening question.
Execute rules and publish only after validation
Our rule selects positive PE below 20. The previous accepted list is dated 2026-10-19 and contains AAA. The October 20 payload has AAA at PE 25 and BBB at PE 18. After refresh and validation, the screen selects BBB. The difference stage reports BBB added and AAA removed. Only then does the object update its accepted result and consume the logical slot screen-v1:2026-10-20.
The actual execution trace is refresh:2026-10-20, validate, screen, diff, publish_in_memory. It is emitted by the methods that perform each step. That makes the trace useful evidence of this script’s control flow; it is not a fabricated summary of what a model claims it did. Keep traces with the result so a reviewer can distinguish a valid calculation from a fluent account of an unexecuted workflow.
rows = self.refresh(day, payload)
self.validate(rows, day)
symbols = self.screen(rows)
# Compute the dated list difference before accepting the result.
Download the complete example. It uses Python 3.12+, the standard library and synthetic data. Save it and run python R550.py. The full program includes calendar and trigger checks, differences, guarded in-memory publication, error results and assertions. There is no background process to enable or disable after running the file.
Separate current failure from a prior success
Inject a provider timeout. The run returns blocked, a reason and current_result: null; the previous accepted result remains dated October 19. Next inject a row dated October 19 into the October 20 payload. Refresh succeeds, but validation blocks the run. Finally remove a required PE field. These failures happen at different steps, and the trace identifies how far execution progressed.
None of those attempts consumes the success slot. A corrected payload can be retried for October 20. This distinction matters: marking a failed attempt done would prevent recovery, while publishing the prior list under a fresh date would create false confidence that current conditions were checked. Preserve the old list as historical context, with its original date and an explicit current-failure state.
The fixture validates the entire small universe before publishing. It does not offer partial-universe publication. If you want a policy that permits partial results, define minimum coverage and exclusion reporting explicitly, with a visibly different status. A partial list should never receive the same success label as a complete execution of the original universe contract.
Handle a closed session and a duplicate trigger
A trigger on October 24 returns closed without calling refresh. A second October 20 trigger after the successful 16:30 run returns duplicate_suppressed, also without calling refresh or publishing again. The deduplication key contains the rule version and intended session date. It is not the arrival time, a random run identifier or the final candidate list, all of which could vary for the same requested update.
This implementation is single-process and keeps state in memory. It does not guarantee exactly-once effects across simultaneous workers, crashes or external notification systems. To extend it, preserve logical-slot state durably and coordinate acceptance with the external effect. Decide how corrected data for a completed session should be reviewed and versioned; silently bypassing deduplication would make the history ambiguous.
Keep enablement separate from a dry-run
The default call is a manual dry-run. Passing dry_run=False while the scheduler flag is disabled returns scheduler_disabled before any data step. The flag is a safety boundary in the demonstration, not an instruction to turn on a real scheduler. Real enablement would require an available scheduler implementation, reviewed credentials and data permissions, durable state, monitoring and a controlled publication destination; none is configured here.
Before relying on an eventual integration, replay the successful session, pre-eligible trigger, closed date, stale payload, missing field, timeout and duplicate slot. Check both status and actual calls. Verify that a repaired failed slot can complete and that earlier results retain their dates. A passing method test should be described precisely as evidence for these local branches, rather than as proof of a running CoTrading or Vibe automation.
Retain a reviewable run record
For every attempt, keep the intended slot, received timestamp, calendar decision, dataset identifiers, validation failures, rule version, candidate list, differences and trace. Save the last accepted result independently of the current attempt. Changes to the universe, rule or data convention should produce a new reviewed version. These records make the shortlist interpretable and provide the input needed for focused recovery when an update fails.
FAQ
Does running the script start a scheduled job?
No. It executes local dry-runs and assertions once, with scheduling disabled.
Can a stale list be displayed after a failure?
Yes, if its original date and the current failure are clear. It must not be labeled a successful new update.