Dosu LogoDosu Logo
Ask
Join our Discord
SurePublic
we-promise
DocumentsSure
Broker Activity Import
Broker Activity Import
Type
Topic
Status
Published
Created
Jul 25, 2026
Updated
Jul 25, 2026

Broker Activity Import#

When a brokerage account syncs, raw activities from the provider API are processed by a per-provider ActivitiesProcessor class that classifies each activity and routes it to one of two record types:

  • Trade (entry with Trade entryable) — for security-level activity with a quantity: buys, sells, reinvestments, option events.
  • Transaction (entry with Transaction entryable) — for pure cash flows: dividends, contributions, withdrawals, fees, transfers, interest.

Both record types share the same investment_activity_label field drawn from a canonical set defined in Transaction::ACTIVITY_LABELS . See Investment Activity Labels for label semantics, analytics impact, and UI badge behavior.

Supported Providers and Processors#

Three providers each have their own ActivitiesProcessor:

ProviderProcessorSource
SnapTrade (Fidelity, Schwab, Robinhood, etc.)SnaptradeAccount::ActivitiesProcessorSnapTrade activity API
IBKRIbkrAccount::ActivitiesProcessorIBKR raw_activities_payload (split into trades + cash_transactions)
Trading212Trading212Account::ActivitiesProcessorThree separate payloads: raw_orders_payload, raw_dividends_payload, raw_transactions_payload

Each processor is called as part of the account's sync pipeline. For SnapTrade, activities are often fetched asynchronously via SnaptradeActivitiesFetchJob due to brokerage sync delays of 30–60+ seconds on fresh connections .

Trade vs. Transaction Split#

Each processor separates incoming activity into Trade and Transaction records at a defined boundary.

SnapTrade uses two explicit constants :

  • TRADE_TYPES — BUY, SELL, REI, REINVEST, OPTION_BUY, OPTION_SELL, EXERCISED, ASSIGNED → import_trade
  • CASH_TYPES — DIVIDEND, DIV, CONTRIBUTION, WITHDRAWAL, TRANSFER_*, INTEREST, FEE, TAX, CASH → import_transaction
  • Unknown types default to "Other" label and go through process_cash_activity .

IBKR receives data pre-split: the payload has a trades key (buy/sell equity trades → import_trade) and a cash_transactions key (dividends, deposits/withdrawals → import_transaction) . Only DEPOSITS/WITHDRAWALS and DIVIDENDS cash types are supported; others are skipped . Commission/fee for each trade is imported as a separate Transaction with label "Fee" .

Trading212 separates at the payload level :

  • raw_orders_payload (filled order records) → import_trade
  • raw_dividends_payload (dividend records) → import_transaction with label "Dividend"
  • raw_transactions_payload (cash events: DEPOSIT, WITHDRAW, INTEREST, FEE) → import_transaction

Label Mapping Per Provider#

Each processor maps provider-native type strings to Sure's canonical investment_activity_label values.

SnapTrade uses SNAPTRADE_TYPE_TO_LABEL, a hash covering 25+ types. Notable non-obvious mappings: TAX → "Fee", STOCK_DIVIDEND → "Dividend", CASH → "Contribution", SPLIT/MERGER/SPIN_OFF → "Other" . Cash amount sign is normalized in normalize_cash_amount: income flows are stored as negative amounts; outflows as positive.

IBKR classifies cash at classify_cash_transaction:

  • DEPOSITS/WITHDRAWALS with positive amount → "Contribution", negative → "Withdrawal"
  • DIVIDENDS → "Dividend" (stored as -amount.abs)
  • Trades are always "Buy" or "Sell" based on the buy_sell field

Trading212 classifies via classify_transaction:

  • DEPOSIT → "Contribution", WITHDRAW → "Withdrawal", INTEREST → "Interest", FEE → "Fee"
  • Orders use order[:side] (BUY/SELL) to set "Buy" or "Sell"
  • Dividends are always "Dividend"

Shared Import Interface: Account::ProviderImportAdapter#

All processors delegate to Account::ProviderImportAdapter, which provides two methods used across all providers:

  • import_trade — creates a Trade-type Entry for security activity
  • import_transaction — creates a Transaction-type Entry for cash flows

Both methods use find_or_initialize_by(external_id:, source:) for idempotent deduplication : re-importing the same activity updates the existing record rather than creating a duplicate. The external_id is provider-namespaced (e.g., ibkr_trade_<trade_id>, trading212_order_<order_id>) to avoid cross-provider collisions .

If a matching external_id exists but with a different entryable type (e.g., a Trade where a Transaction is expected), the adapter raises an ArgumentError to prevent type collisions . Records flagged as user-modified or import-locked are not overwritten.

Key Source Files#

FilePurpose
app/models/snaptrade_account/activities_processor.rbSnapTrade type→label mapping, TRADE_TYPES/CASH_TYPES split
app/models/ibkr_account/activities_processor.rbIBKR trade + cash transaction processing, commission import
app/models/trading212_account/activities_processor.rbTrading212 orders, dividends, and cash transaction processing
app/models/account/provider_import_adapter.rbShared import_trade / import_transaction with deduplication
Investment Activity LabelsFull label set, budget/analytics impact, UI badge behavior
SnapTrade IntegrationSnapTrade sync pipeline and async activities fetch
Dividend and DRIP ModelingTrade vs. Transaction distinction for dividends; DRIP reinvestment
Documents
Account Authorization and Permissions
Account Balance Calculation
Account Creation
Account Lifecycle Management
Account Provider Architecture
Account Reporting Controls
Account Statement Management
Account Statement Reconciliation
Account Type Architecture
AI Bank Statement Extraction
AI Chat Interface
API Authentication and Authorization
Authentication and Session Management
Balance History System
Banking Data Encryption
Banking Provider Integration
Brandfetch Logo Integration
Broker Activity Import
Budget Management
Category Management
Cryptocurrency Account Management
CSV Import and Column Mapping
Currency Management
Dashboard Filtering and Drilldowns
Depository Yield Modeling
Dev Container Setup
Dividend and DRIP Modeling
Docker Self-Hosting
Enable Banking Consent Management
Enable Banking Error Handling
Enable Banking OAuth and PSD2 Authentication
Entry Rendering
Family Data Export
Family Settings Management
Financial Insights and Metrics
Financial Reporting
FIRE Planning and Retirement Calculations
Goals and Savings Tracking
Internationalization and Localization
Investment Account Data Pipeline
Investment Account Flow Semantics
Investment Account Reconciliation
Investment Activity Labels
Investment Holdings Management
Investment Tax Treatment Classification
Investment Trade Conversion
Investment Trade Entry
Invitation Lifecycle and State Management
Ledger Entry Accounting Model
LLM Provider Configuration
LLM Request Timeout and Watchdog System
LLM Tool Calling
Manual Account Entry and Import
Manual Valuation
MCP Tool Access
Merchant Data Enhancement
Merchant Data Model
Multi-Currency Exchange Rates
NDJSON Import System
Net Worth Balance Sheet
OIDC Provider Configuration
Pending Transaction Reconciliation
Plaid Integration
Portfolio Cache and Price Resolution
Provider Import Adapter
Rails Development Environment Configuration
Rails PWA Integration
Recurring Transactions and Cash Flow Projection
REST API Architecture
Securities Lookup
Security Exchange Identification
Security Price Import Pipeline
SimpleFIN Holdings Import
SimpleFIN Integration
SimpleFIN Liability Balance Normalization
SnapTrade Integration
Split Transactions
SSO Audit Logging
SSO Authentication Flow
SSO Provider Management
Timezone-Aware Financial Data Handling
Transaction Categorization
Transaction Deduplication
Transaction Exclusion
Transaction Filtering and Search
Transaction Management
Transaction Name Resolution
Transaction Rule Engine
Transaction Sync Windows and Lookback
Transfer Management
Transfer Matching and Pairing
Turbo Frame Navigation
Yahoo Finance Integration
How split transaction child exclusion was implemented
Is it possible to edit the date of a synced transaction (e.g., one synced by LunchFlow)?
Provider Architecture