Helpers¶
Time-based utilities for periodic operations: detect modulo-aligned boundaries (every 4 hours from midnight, every 15 minutes from the hour, …) without writing scheduling boilerplate. Designed for trading automation, scheduled restarts, and long-running loops where alignment to wall-clock boundaries matters more than fixed intervals.
Tip
For the API reference of the underlying classes (TimeTrigger, TimeTriggerError, InvalidModuloError),
see Helpers.
TL;DR¶
from kstlib.helpers import TimeTrigger
# Trigger at 4-hour boundaries: 00:00, 04:00, 08:00, 12:00, 16:00, 20:00
trigger = TimeTrigger("4h")
if trigger.is_at_boundary():
rotate_log_files()
# Margin-based: trigger 60s before the boundary so a long task finishes on time
if trigger.should_trigger(margin=60):
restart_websocket()
trigger.record_trigger() # tracked in stats
# Look-ahead helpers
print(trigger.time_until_next()) # seconds until next 4h boundary
print(trigger.next_boundary_time()) # datetime of next 4h boundary
Key Features¶
Modulo-aligned scheduling: boundaries are anchored to wall-clock divisions, not to “N seconds from now”.
Human-readable intervals:
"4h","15m","1d","30s", or a raw integer in seconds.Margin support: trigger a configurable number of seconds before the boundary (early-fire) for tasks that need lead time.
Statistics tracking:
record_trigger()accumulates counts and timestamps so you can audit how often a boundary actually fired.Context manager:
with TimeTrigger("1h") as trigger:cleans up internal state for short-lived loops.
Common patterns¶
Scheduled WebSocket restart¶
import asyncio
from kstlib.helpers import TimeTrigger
from kstlib.websocket import WebSocketManager
trigger = TimeTrigger("8h")
async def maybe_restart(ws: WebSocketManager) -> None:
"""Restart WebSocket at 8-hour boundaries for stability."""
if trigger.should_trigger(margin=30):
await ws.shutdown()
await ws.connect()
trigger.record_trigger()
Trading candle close detection¶
from kstlib.helpers import TimeTrigger
candle_15m = TimeTrigger("15m")
while running:
if candle_15m.is_at_boundary():
process_15m_candle_close()
candle_15m.record_trigger()
await asyncio.sleep(1)
Interval formats¶
Format |
Meaning |
|---|---|
|
30 seconds |
|
15 minutes |
|
4 hours |
|
1 day (24 hours) |
|
3600 seconds (raw int) |