A financial news feed should reflect more than a customer’s holdings. Two investors may own the same assets but differ in search intent and risk-related behavior. Financial DNA is a precomputed user vector that combines semantic signals from portfolios and searches with normalized behavioral features, giving personalization systems a richer representation of the user’s current context. section 13.5 section 13.0
Build and use the vector in separate stages:
- Unify ledger records of trades, balances, and cash flows with clickstream events in an offline User 360 feature pipeline. Daily aggregation avoids querying those systems for every recommendation. section 13.1.2
- Embed portfolio and interest terms, then combine their semantic representation with normalized behavioral features. Choose an embedding model with financial-domain understanding while balancing vector resolution against cost and refresh speed. section 13.2.1 section 13.5
- At query time, retrieve the stored user vector and match its semantic portion against content vectors made with the same embedding model. Use the separate behavioral dimensions for ranking and guardrails, not for content-vector cosine similarity. section 13.3.2
A new financial news feed
A newly launched feed can rank articles without waiting for feed-interaction history. In the illustrated setup, the user’s 1,541-dimensional DNA contains a 1,536-dimensional semantic portion compatible with article embeddings; the remaining five behavioral dimensions have no article-side counterpart. Cosine similarity ranks candidate articles by semantic relevance, while suitability controls can override inappropriate recommendations. section 13.3.2 section 13.5
Listing 13.8 The semantic matching engine [2]
import numpy as np
import pickle
from sklearn.metrics.pairwise import cosine_similarity
# 1. Setup: Load the "Mock Vector DB"
# (In a real app, this would be a connection to Pinecone/Milvus)
# We assume 'content_store.pkl' contains the list of vectorized articles generated in the pipeline.
with open("content_store.pkl", "rb") as f:
content_store = pickle.load(f)
def get_recommendations(user_id, content_list, top_k=2):
# 2. Fetch the User Vector (Pre-computed Financial DNA)
user_row = users.loc[users['user_id'] == user_id]
if user_row.empty:
return []
# Reshape for scikit-learn (1 sample, 1536 features)
user_vec = np.array(user_row.iloc[0]['financial_dna']).reshape(1, -1)
# 3. Score Content Alignment
results = []
for item in content_list:
content_vec = np.array(item['vector']).reshape(1, -1)
# Cosine Similarity: 1.0 = Perfect Match, 0.0 = No Relation
score = cosine_similarity(user_vec, content_vec)[0][0]
results.append((item['title'], item['type'], score))
# 4. Rank by Relevance
return sorted(results, key=lambda x: x[2], reverse=True)[:top_k]
# --- Test Scenario: Comparing Tribes ---
# We select representative users from our clusters to validate the logic.
degen_user = users.loc[users['cluster_id'] == 0, 'user_id'].iloc[0] # Cluster 0: Degen
saver_user = users.loc[users['cluster_id'] == 3, 'user_id'].iloc[0] # Cluster 3: Saver
print(f"--- Recommendations for Degen User ({degen_user}) ---")
for title, ctype, score in get_recommendations(degen_user, content_store):
print(f"[{ctype}] {title} (Score: {score:.3f})")
print(f"\n--- Recommendations for Saver User ({saver_user}) ---")
for title, ctype, score in get_recommendations(saver_user, content_store):
print(f"[{ctype}] {title} (Score: {score:.3f})")
The matching-engine listing shows the query-time pattern: fetch precomputed DNA, score vectorized content, and return ranked candidates. Compare only compatible semantic dimensions. section 13.3.2
The same representation can support clustering, similarity search, and risk monitoring. Risk, operations, product, and marketing teams can use it as a shared view of user state. Monitor downstream outcomes rather than click-through alone: recommendations associated with negative outcomes should inform feature design, labeling, and data collection. section 13.2 section 13.4.3 section 13.5
