Trade Filters
Every per-relationship filter that can skip a copy before it's replicated, evaluated together by RiskCalculationService.checkTradeFilters (src/lib/copy-engine/risk-calculation-service.ts), short-circuiting on the first one that blocks. Symbol allow/deny (isSymbolAllowed) and the risk-profile-level filters (Max Open Trades, Time Filter, News Filter, Max Position Size) are evaluated separately — see docs/RISK_PROFILES.md — since they only ever apply to 'open' events, unlike the filters below which apply to every event type.
| Filter | Column(s) | Pure function | Applies to |
|---|---|---|---|
| Trade Direction | direction_filter | checkDirectionFilter | Buy Only / Sell Only / Both |
| Trading Session/Days | risk_profiles.time_filter | checkTimeFilter | see docs/RISK_PROFILES.md |
| Maximum Spread | max_spread_points | checkSpreadFilter | skips if the follower's live spread exceeds this |
| Maximum Slippage | max_slippage_points | checkSlippageFilter | skips if price moved too far since the signal |
| Copy Pending Orders | copy_pending_orders | checkPendingOrderFilter | opt-in; position events always eligible |
| Magic Number | magic_numbers (integer[]) | checkMagicNumberFilter | only copy signals tagged with one of these |
| Comment Contains | comment_filter (text) | checkCommentFilter | only copy signals whose comment contains this substring |
Magic Number / Comment — what's real and what isn't
StandardTrade.magicNumber/.comment (src/lib/connectors/sdk/trade.ts) are populated only by the MT5 connector today (src/lib/connectors/mt5/normalize.ts, reading Mt5Position/Mt5Order/ Mt5Deal's own magic/comment fields — confirmed real fields on the installed metaapi.cloud-sdk's own MetatraderPosition/ MetatraderOrder/MetatraderDeal types, not guessed). "Magic number" is MT4/MT5's own concept (identifies which Expert Advisor opened a position); no other broker this codebase connects to has an equivalent first-class field, so every other connector's StandardTrade output leaves both fields undefined.
A relationship whose master isn't MT5 can still configure a Magic Number or Comment filter — it will simply never match (every non-MT5 signal has no magic number/comment to compare against a configured filter, and checkMagicNumberFilter/checkCommentFilter both treat "the signal has none but the relationship requires one" as a block, not a pass). This is the correct, honest behavior: a filter that can't be evaluated shouldn't silently let everything through.
Configured per relationship in the Copy Relationship dialog (src/components/copier/copy-relationship-dialog.tsx) as a comma- separated magic-number list and a free-text comment substring, parsed by readTradeFilters in src/app/dashboard/copier/actions.ts.
Database
copy_relationships.magic_numbers integer[] and copy_relationships.comment_filter text (migration 20260808030000_magic_number_comment_filters.sql) — both nullable, additive, default to "no restriction" for every relationship that existed before this migration.