feat: implement SniperDetector with DBSCAN clustering (#16)
Add sniper cluster detection system that identifies wallets exhibiting coordinated "sniper" behavior - consistently entering markets within minutes of their creation. Key features: - SniperDetector class using DBSCAN clustering algorithm - Tracks wallet entries across markets with timing analysis - Feature vector: market hash, entry delta, log position size - Identifies clusters of wallets with similar timing patterns - Generates SniperClusterSignal for detected cluster members - Configurable entry threshold (default 5 minutes), cluster size, DBSCAN params - Confidence scoring based on cluster size, entry speed, market overlap Also adds: - SniperClusterSignal model to detector/models.py - scikit-learn and numpy dependencies for ML clustering - 27 comprehensive tests covering clustering logic and edge cases 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
c100bbfdfb
commit
96e471882a
@@ -148,6 +148,54 @@ class SizeAnomalySignal:
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SniperClusterSignal:
|
||||
"""Signal emitted when a wallet is identified as part of a sniper cluster.
|
||||
|
||||
Sniper clusters are groups of wallets that consistently enter markets
|
||||
within minutes of their creation, suggesting coordinated insider activity.
|
||||
|
||||
Attributes:
|
||||
wallet_address: The wallet identified as a sniper.
|
||||
cluster_id: Unique identifier for this cluster.
|
||||
cluster_size: Number of wallets in the cluster.
|
||||
avg_entry_delta_seconds: Average time (seconds) from market creation to entry.
|
||||
markets_in_common: Number of markets where cluster members overlap.
|
||||
confidence: Confidence score (0.0 to 1.0) based on clustering strength.
|
||||
timestamp: When this signal was generated.
|
||||
"""
|
||||
|
||||
wallet_address: str
|
||||
cluster_id: str
|
||||
cluster_size: int
|
||||
avg_entry_delta_seconds: float
|
||||
markets_in_common: int
|
||||
confidence: float
|
||||
timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))
|
||||
|
||||
@property
|
||||
def is_high_confidence(self) -> bool:
|
||||
"""Return True if confidence exceeds 0.7."""
|
||||
return self.confidence >= 0.7
|
||||
|
||||
@property
|
||||
def is_very_high_confidence(self) -> bool:
|
||||
"""Return True if confidence exceeds 0.85."""
|
||||
return self.confidence >= 0.85
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
"""Serialize to dictionary for Redis stream publishing."""
|
||||
return {
|
||||
"wallet_address": self.wallet_address,
|
||||
"cluster_id": self.cluster_id,
|
||||
"cluster_size": self.cluster_size,
|
||||
"avg_entry_delta_seconds": self.avg_entry_delta_seconds,
|
||||
"markets_in_common": self.markets_in_common,
|
||||
"confidence": self.confidence,
|
||||
"timestamp": self.timestamp.isoformat(),
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RiskAssessment:
|
||||
"""Combined risk assessment aggregating all signal types.
|
||||
|
||||
Reference in New Issue
Block a user