Official Python SDK for Discourses โ Era-calibrated financial sentiment analysis
Project description
Discourses Python SDK
Institutional-grade financial sentiment analysis with era-calibrated lexicons
๐ Read the Whitepaper ย ย ยทย ย ๐ฌ Methodology ย ย ยทย ย ๐ Documentation ย ย ยทย ย ๐ Get API Key
Why Discourses?
Traditional sentiment analysis treats language as static. But financial language evolvesโ"disruption" meant crisis in 2008, innovation by 2020. Discourses solves this with era-calibrated lexicons built on peer-reviewed academic research.
| Era | Period | What Changed |
|---|---|---|
| ERA_1 | 2007โ2011 | Crisis vocabulary, early social trading |
| ERA_2 | 2012โ2015 | QE-era optimism, regulatory terminology |
| ERA_3 | 2016โ2019 | Algorithmic trading, crypto emergence |
| ERA_4 | 2020โnow | Pandemic markets, retail revolution, meme stocks |
Installation
pip install discourses
Quick Start
from discourses import Discourses
client = Discourses(api_key="your-api-key")
result = client.analyze("Apple reported record quarterly earnings")
print(result.sentiment) # "positive"
print(result.score) # 0.72
API Reference
๐ Initialize the Client
Every request requires authentication with your API key. Get yours at discourses.io/dashboard.
from discourses import Discourses
client = Discourses(api_key="your-api-key")
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key |
str |
required | Your Discourses API key |
base_url |
str |
https://discourses.io/api/v1 |
API base URL |
timeout |
int |
30 |
Request timeout in seconds |
Endpoints
๐ Analyze โ Single Text Sentiment
POST /analyzeโ Documentation
Analyze sentiment of any financial text using an era-specific lexicon. Returns a score from -1.0 (bearish) to +1.0 (bullish) with confidence metrics.
from discourses import Discourses, Era
# Initialize client
client = Discourses(api_key="your-api-key")
# Analyze text with modern lexicon (default)
result = client.analyze(
text="Tesla exceeds delivery expectations, stock surges in after-hours trading",
era=Era.ERA_4
)
# Access the results
print(f"Score: {result.score}")
print(f"Sentiment: {result.sentiment}")
print(f"Magnitude: {result.magnitude}")
print(f"Confidence: {result.confidence}")
print(f"Words: {result.word_count}")
Expected Output:
Score: 0.78
Sentiment: positive
Magnitude: 0.82
Confidence: 0.94
Words: 11
Response Object: AnalysisResult
| Field | Type | Description |
|---|---|---|
score |
float |
Sentiment score from -1.0 to +1.0 |
sentiment |
str |
"positive", "negative", or "neutral" |
magnitude |
float |
Strength of sentiment (0.0 to 1.0) |
confidence |
float |
Model confidence (0.0 to 1.0) |
era |
str |
Era lexicon used |
word_count |
int |
Number of words analyzed |
matches |
list |
Sentiment words found with scores |
Helper Properties:
result.is_positiveโTrueif sentiment is positiveresult.is_negativeโTrueif sentiment is negativeresult.is_neutralโTrueif sentiment is neutral
๐ Compare โ Cross-Era Analysis
POST /compareโ Documentation
Analyze how the same text would be interpreted across different market eras. Essential for backtesting, historical analysis, and understanding semantic drift.
from discourses import Discourses
# Initialize client
client = Discourses(api_key="your-api-key")
# Compare sentiment across all eras
comparison = client.compare(
text="Aggressive disruption strategy threatens incumbent market leaders"
)
# View per-era sentiment
print("Era-by-Era Sentiment:")
print("-" * 40)
for era_result in comparison.results:
bar = "โ" * int(abs(era_result.score) * 20)
sign = "+" if era_result.score >= 0 else "-"
print(f" {era_result.era}: {sign}{era_result.score:5.2f} {bar}")
# Check semantic drift
print()
print(f"Semantic Drift: {comparison.drift.max_drift:.2f}")
print(f"Trend: {comparison.drift.trend}")
Expected Output:
Era-by-Era Sentiment:
----------------------------------------
era_1: -0.45 โโโโโโโโโ
era_2: -0.22 โโโโ
era_3: +0.18 โโโ
era_4: +0.61 โโโโโโโโโโโโ
Semantic Drift: 1.06
Trend: increasing
๐ก Notice how "disruption" shifted from negative (crisis-era) to positive (innovation-era) across time periods.
Response Object: CompareResult
| Field | Type | Description |
|---|---|---|
text |
str |
Original analyzed text |
results |
List[EraResult] |
Sentiment for each era |
drift |
DriftAnalysis |
Drift metrics across eras |
DriftAnalysis Fields:
| Field | Type | Description |
|---|---|---|
max_drift |
float |
Maximum score difference between any two eras |
min_score |
float |
Lowest score across eras |
max_score |
float |
Highest score across eras |
mean_score |
float |
Average score |
trend |
str |
"increasing", "decreasing", or "stable" |
Helper Properties:
comparison.drift.has_significant_driftโTrueif drift exceeds 0.2
โก Batch โ High-Volume Processing
POST /batchโ Documentation
Efficiently analyze up to 100 texts in a single request. Supports both single-era analysis and cross-era comparison mode.
from discourses import Discourses, Era
# Initialize client
client = Discourses(api_key="your-api-key")
# Headlines to analyze
headlines = [
"Fed signals potential rate cuts amid cooling inflation",
"Tech layoffs accelerate as recession fears mount",
"Nvidia beats estimates on unprecedented AI chip demand",
"Regional bank failures spark contagion concerns",
"Consumer spending resilient despite economic headwinds",
]
# Batch analyze with ERA_4 lexicon
batch = client.batch(texts=headlines, era=Era.ERA_4)
# Process results
print("Headline Sentiment Analysis")
print("=" * 60)
for item in batch:
emoji = "๐ข" if item.result.is_positive else "๐ด" if item.result.is_negative else "โช"
print(f"{emoji} [{item.result.score:+.2f}] {item.text[:50]}...")
print()
print(f"Processed: {batch.success_count}/{batch.total_count}")
print(f"Bullish: {sum(1 for i in batch if i.result.is_positive)}")
print(f"Bearish: {sum(1 for i in batch if i.result.is_negative)}")
Expected Output:
Headline Sentiment Analysis
============================================================
๐ข [+0.52] Fed signals potential rate cuts amid cooling inf...
๐ด [-0.68] Tech layoffs accelerate as recession fears mount...
๐ข [+0.84] Nvidia beats estimates on unprecedented AI chip ...
๐ด [-0.73] Regional bank failures spark contagion concerns...
๐ข [+0.31] Consumer spending resilient despite economic hea...
Processed: 5/5
Bullish: 3
Bearish: 2
Batch with Cross-Era Comparison
# Enable cross-era comparison for each text
batch = client.batch(
texts=headlines,
compare_eras=True # Analyze each text across all eras
)
# Access drift for each headline
for item in batch:
drift = item.comparison.drift
print(f"'{item.text[:40]}...'")
print(f" โ Drift: {drift.max_drift:.2f} ({drift.trend})")
Response Object: BatchResult
| Field | Type | Description |
|---|---|---|
items |
List[BatchItem] |
Results for each text |
total_count |
int |
Total texts submitted |
success_count |
int |
Successfully processed |
error_count |
int |
Failed to process |
BatchItem Fields:
| Field | Type | Description |
|---|---|---|
index |
int |
Position in original list |
text |
str |
The analyzed text |
result |
AnalysisResult |
Single-era result (if compare_eras=False) |
comparison |
CompareResult |
Multi-era result (if compare_eras=True) |
error |
str |
Error message if failed |
Helper Methods:
batch.get_successful()โ List of successful itemsbatch.get_failed()โ List of failed itemsbatch.all_succeededโTrueif no errorslen(batch)โ Number of itemsbatch[0]โ Access by indexfor item in batchโ Iterate over items
Error Handling
The SDK provides typed exceptions for robust error handling:
from discourses import (
Discourses,
AuthenticationError,
RateLimitError,
ValidationError,
DiscoursesError,
)
client = Discourses(api_key="your-api-key")
try:
result = client.analyze("Market analysis text")
except AuthenticationError:
# Invalid or expired API key
print("Check your API key at https://discourses.io/dashboard")
except RateLimitError as e:
# Too many requests
print(f"Rate limited. Retry after {e.retry_after} seconds")
except ValidationError as e:
# Invalid input (empty text, too long, etc.)
print(f"Invalid input: {e.message}")
except DiscoursesError as e:
# Catch-all for other API errors
print(f"API error: {e}")
Era Selection Guide
| Use Case | Recommended Era |
|---|---|
| Real-time market sentiment | Era.ERA_4 |
| Backtesting 2020+ strategies | Era.ERA_4 |
| Backtesting 2016โ2019 | Era.ERA_3 |
| Historical crisis analysis | Era.ERA_1 |
| Understanding semantic drift | client.compare() |
from discourses import Era
# Modern analysis (default)
client.analyze(text, era=Era.ERA_4)
# Historical analysis
client.analyze(text, era=Era.ERA_1)
# String values also work
client.analyze(text, era="era_2")
Links
| ๐ | Whitepaper Academic research & methodology |
๐ | Documentation Full API reference |
| ๐ฌ | Methodology How era-calibration works |
๐ | Dashboard Get your API key |
| ๐ฌ | Issues Report bugs & requests |
๐ฆ | PyPI Package & versions |
License
MIT License โ see LICENSE for details.
Built with โค๏ธ by Discourses
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file discourses-1.0.0.tar.gz.
File metadata
- Download URL: discourses-1.0.0.tar.gz
- Upload date:
- Size: 18.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef1ae6d4bb9b711218409803e04a192fe659896b82632559d8b8fa6a73364df7
|
|
| MD5 |
a8cbd2bfe927ffc68019651c07936aa5
|
|
| BLAKE2b-256 |
c9b9d75b8b9451ac0ee29d23bd61e2fb6a51519298bbcb2e9c7bfdb80e09d891
|
File details
Details for the file discourses-1.0.0-py3-none-any.whl.
File metadata
- Download URL: discourses-1.0.0-py3-none-any.whl
- Upload date:
- Size: 16.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ee4d87fcfb81494b76cfd3b288f8a66988131544104d20f0bbabc6a84849da1
|
|
| MD5 |
7f64bcfae3291d0d051f1b3464f9c76e
|
|
| BLAKE2b-256 |
a5b27186a99ca0e0a6263d97cb5fdbd4af5693d9d9564f94d9d0551dcae884dd
|