Quant Trading for Programmers 26: Abstract Notification Channels First
· 11 min read · Views --
Last updated on

Quant Trading for Programmers 26: Abstract Notification Channels First

Author: Alex Xiang


Quant Trading for Programmers 26: Abstract Notification Channels First

Article 25 added production checks to the daily flow. The next step is to send the daily report out.

Article 26 does not connect Lark or email yet. It first creates the notification-channel abstraction. Generating a message is one concern. Sending it somewhere and knowing whether it was accepted is another.

A ZiCode engineer organizing paper-trading notification channels

Stabilize The Send Interface First

Notification channels are easy to hard-code too early.

Today the report goes to a file. Tomorrow it may need to go to Lark. The day after that it may need email. If the daily report generation logic depends directly on one concrete channel, later changes become awkward.

Receipt Object

Chapter 26 adds app/notification_channels.py.

@dataclass(frozen=True)
class NotificationReceipt:
    channel: str
    accepted: bool
    destination: str
    message_title: str
    sent_at: datetime
    error: str = ""

Every send action must have a receipt. Without a receipt, later health reports cannot judge whether the alert was actually accepted by the channel.

“Accepted” here only means the channel layer agreed to accept this message. It does not mean a reader has already seen it. In a real system, this is usually split further into send success, platform receipt success, read status, and so on. Article 26 keeps only the minimum semantic boundary: whether the message was accepted by the channel.

Channel Protocol

class NotificationChannel(Protocol):
    name: str

    def send(self, message: PaperAlertMessage, *, destination: str, sent_at: datetime) -> NotificationReceipt:
        ...

This protocol is intentionally small. It only constrains the input message, destination, and send time. A concrete channel can be a file, Lark, email, or an in-memory implementation for tests.

Current Integrated Run

Articles 26-30 can now run through one command:

uv run python -m scripts.chapter_examples paper-notify

The command generates target weights, fetches prices, runs the daily flow, and sends the daily report to an in-memory channel and a file channel. Article 26 focuses on the channel protocol and receipt:

Notification-channel receipt generated by the paper-notify command

In this run, the in-memory channel returns accepted=True, and the destination is paper-daily. This result later enters the run health report as evidence for whether the daily report was actually delivered to a channel.

Chapter Update And Repository

This chapter adds:

  • app/notification_channels.py.
  • A notification receipt and channel protocol.
  • MemoryNotificationChannel for offline tests.
  • An integrated paper-notify example showing the channel receipt after the daily report is generated.
  • A semantic boundary between channel acceptance and user read status.
  • tests/test_notification_channels.py, covering valid messages, missing destinations, and empty message bodies.

Repository:

https://github.com/ax2/zi-quant-platform

Code for this chapter:

git clone https://github.com/ax2/zi-quant-platform.git
cd zi-quant-platform
git checkout chapter-26
uv sync --extra dev
uv run pytest tests/test_notification_channels.py

Chapter 26 is commit 6bf1a6f, tagged as chapter-26.

Summary

Notification channels need a boundary before they connect to concrete platforms.

Article 26 stabilizes the send interface and receipt structure. The next article implements a file-based channel so daily report sending can be verified and replayed locally.