Skip to content

(Feature Request) Per-Message Header Fields in Favourites (OnBehalfOfCompID) and Header/Trailer Visibility in Message Viewer & Diff #63

Description

@klarflow

Description

Two related gaps exist around FIX message header/trailer handling:

1. Header fields (e.g. OnBehalfOfCompID) are only configurable at the session/profile level, not per Favourite

Currently, header fields like OnBehalfOfCompID (tag 115) are configured globally via the Header Fields section in GeneralInfo.tsxFixHeaderForm. These values are stored in session.profile.headerFields and applied uniformly to every outgoing message in encodeToFix(). There is no way to save different header field values per Favourite.

In real trading workflows, different orders are often placed on behalf of different clients. A trader managing orders for Client A and Client B needs OnBehalfOfCompID to vary per order, not be locked to one value for the entire session. Today, the only workaround is to manually change the global header config before sending each message — error-prone and slow.

2. Header and trailer fields are not visible in the structured Message Viewer or in-session Message Diff

When viewing received/sent messages, the structured table view (MessageView) only shows body fields via getValue(). Header fields like SenderCompID, TargetCompID, OnBehalfOfCompID, SendingTime, etc. are invisible in the structured view (only visible in the raw message string). The same gap applies to the in-session diff comparison.

Current Behavior

Favourites — header fields not saved:

In FavoriteManagementService.ts (line 32), only body data is persisted:

            await this.fileManager.writeFile(`${saveDirPath}/${name}___${messageDef.name}.json`, JSON.stringify({ msg: messageDef.name, data }));

And in FixForm.tsx (lines 214–220), getMessageData() extracts only body fields:

    private onAddToFavorites = (name: string) => {
        const { session, message } = this.props;
        const data = this.formRef.current?.getFieldsValue();
        const ret = this.getMessageData(data);
        this.setState({ saving: true })

        GlobalServiceRegistry.favoriteManager.addToFavorites((session as FixSession).profile, message, name, ret).then(() => {

When a Favourite is loaded and sent, it goes through encodeToFix() which only applies profile-level headers:

    public encodeToFix = (header: FixMsgHeader, msgDef: FixMessageDef, parameters?: Parameters, additionalHeaders?: any): string => {
        let newHeaders: any = {};
        if (this.profile.headerFields) {
            newHeaders = { ...this.profile.headerFields }
        }

        if (additionalHeaders) {
            newHeaders = { ...newHeaders, ...additionalHeaders }
        }
        
        return this.parser.encodeToFix(msgDef, msgDef.getValue(), header, parameters, newHeaders)
    }

MessageView — body only:

In MessageView.tsx (line 166), the structured view renders only getValue() (body fields):

        {!this.isEmpty(selectedMsg.def.getValue()) ? <div id={`search-node-msg-view${this.componentKey}`}>{this.renderValues(selectedMsg.def.getValue())}</div>
          : <div className="no-data">{getIntlMessage("no_data")}</div>}

In-session Diff — body only:

In SessoinMessageStream.tsx (line 177), when selecting two messages for diff, the raw JSON is constructed from body-only data:

      this.setState({
        selectedRows: rows.map((row: any) => ({
          def: row.msg,
          rawMsg: JSON.stringify(row.msg.getValue(), null, 2),
          session: this.props.session
        })), diffModeEnabled: true

This feeds into MessageDiffViewerReactDiffViewer, so the diff comparison only covers body fields.

Note: The standalone Message Diff Viewer tab (MessageDiffViewerManagement.tsx line 117) does use getValueWithHeaders() for its comparison, so it includes header fields. But the side-by-side mode there still uses MessageView, which falls back to body-only.

Expected Behavior

1. Per-Favourite header field overrides:

  • When editing/saving a Favourite, allow the user to optionally specify header field overrides (e.g. OnBehalfOfCompID = "ClientA").
  • The FavoriteInstance structure should support an optional headerOverrides field:
    export interface FavoriteInstance {
        msg: string,
        data: any,
        headerOverrides?: any  // optional per-favourite header fields
    }
  • When sending a Favourite, its headerOverrides should be merged on top of profile.headerFields (with Favourite values taking precedence), before passing to encodeToFix().
  • The Favourite form (or a collapsible section within it) should expose the relevant header fields for override.

2. Header/trailer visibility in MessageView:

  • MessageView should display header fields (and optionally trailer fields) in the structured table, perhaps in a collapsible "Header" section above the body and a "Trailer" section below, using getValueWithHeaders() or a new method that separates header/body/trailer.
  • This should apply everywhere MessageView is used: the session message viewer panel, the MessageViewer tab, and the side-by-side diff.

3. In-session diff should include headers:

  • In SessoinMessageStream.tsx, the rawMsg for diff should use getValueWithHeaders() (or equivalent) instead of getValue() so that header differences are visible in the diff view.

Files to Modify

File Change
src/services/favorite-management/FavoriteManagementService.ts Extend FavoriteInstance with optional headerOverrides; save/load them
src/main-layout/SessionWindow/SessionTab/SessionManagement/FixForm.tsx Add UI for editing header overrides when saving a Favourite; pass headerOverrides through to encodeToFix() on send
src/services/fix/FixSession.ts encodeToFix() — accept and merge per-message header overrides (Favourite-level) between profile headers and additional headers
src/services/fix/FixDefs.ts Potentially add a method to separate header, body, and trailer data for structured display
src/common/MessageView/MessageView.tsx Use getValueWithHeaders() (or a structured variant) to render header/trailer sections in the table view
src/main-layout/SessionWindow/SessionTab/SessoinMessageStream/SessoinMessageStream.tsx Use getValueWithHeaders() instead of getValue() when constructing diff JSON (line 177)
src/main-layout/SessionWindow/SessionTab/SessoinMessageStream/MessageDiffViewer.tsx Ensure the side-by-side mode also shows header/trailer
src/services/profile/ProfileDefs.ts No structural change needed — headerFields remains the session-level default

Additional Context

  • Current summary of header/trailer visibility across the app:

    Component Shows Headers? Shows Trailers? Data Source
    FixForm (message editor) No No Body only
    FixHeaderForm (global config) Yes No Profile-level headerFields
    MessageView (structured view) No No getValue() — body only
    In-session Diff No No getValue() — body only
    Standalone Diff tab (JSON diff) Yes Partially getValueWithHeaders()
    Standalone Diff tab (side-by-side) No No Falls back to MessageView
    Raw message display Yes Yes Full raw FIX string
  • The decodeInternals() method in FixDefinitionParser.ts already populates dataWithHeaders (with fieldName[tagNumber] format) alongside the body-only data. The infrastructure to expose header fields is partially there — it just needs to be surfaced in the UI components.

  • This feature is particularly important for institutional trading desks where a single session handles orders for multiple underlying clients, making OnBehalfOfCompID a per-order concern rather than a session-level setting.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions