Skip to main content

Official Python SDK for Discourses โ€” Era-calibrated financial sentiment analysis

Project description

Discourses

Discourses Python SDK

Institutional-grade financial sentiment analysis with era-calibrated lexicons

PyPI version Python 3.8+ License: MIT

๐Ÿ“„ 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 โ†’ True if sentiment is positive
  • result.is_negative โ†’ True if sentiment is negative
  • result.is_neutral โ†’ True if 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 โ†’ True if 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 items
  • batch.get_failed() โ†’ List of failed items
  • batch.all_succeeded โ†’ True if no errors
  • len(batch) โ†’ Number of items
  • batch[0] โ†’ Access by index
  • for 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

discourses-1.0.0.tar.gz (18.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

discourses-1.0.0-py3-none-any.whl (16.9 kB view details)

Uploaded Python 3

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

Hashes for discourses-1.0.0.tar.gz
Algorithm Hash digest
SHA256 ef1ae6d4bb9b711218409803e04a192fe659896b82632559d8b8fa6a73364df7
MD5 a8cbd2bfe927ffc68019651c07936aa5
BLAKE2b-256 c9b9d75b8b9451ac0ee29d23bd61e2fb6a51519298bbcb2e9c7bfdb80e09d891

See more details on using hashes here.

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

Hashes for discourses-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4ee4d87fcfb81494b76cfd3b288f8a66988131544104d20f0bbabc6a84849da1
MD5 7f64bcfae3291d0d051f1b3464f9c76e
BLAKE2b-256 a5b27186a99ca0e0a6263d97cb5fdbd4af5693d9d9564f94d9d0551dcae884dd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page