# bitcoin-heuristics
[](https://crates.io/crates/bitcoin-heuristics)
[](https://docs.rs/bitcoin-heuristics)
[](https://github.com/rodrigoescorsim/bitcoin-heuristics/actions)
[](LICENSE)
Pattern detection heuristics for Bitcoin on-chain analytics.
Detects consolidations, distributions, CoinJoin, fee spikes, dormant supply reactivation, and more — from pre-extracted [`TxFeatures`](https://docs.rs/bitcoin-heuristics/latest/bitcoin_heuristics/models/struct.TxFeatures.html). No blockchain node required for most heuristics.
## Features
- **8 bundled heuristics** — consolidation, distribution, CoinJoin detection, urgent execution (fee spike), address reuse, round value, HFT consolidation, long-term supply activation
- **6 analyzer modules** — topology, UTXO age classification, change detection, fee percentile, Lightning detection, BTC price encoding
- **20 classification enums** — FeeRateTier, StructuralPattern, ScriptType, CoinJoinScore, LightningIndicator, and more
- **Zero async runtime** — synchronous design, `ureq` for optional RPC calls
- **No database dependencies** — pure computation on `TxFeatures`, works standalone
- **Extensible** — implement the `Heuristic` trait to add custom detectors
## Quick Start
```toml
[dependencies]
bitcoin-heuristics = "0.1"
```
```rust
use bitcoin_heuristics::{HeuristicRegistry, TxFeatures};
let features = TxFeatures {
input_count: 15,
output_count: 2,
is_consolidation: true,
total_input_value: 200_000_000, // 2 BTC in satoshis
is_input_script_homogeneous: true,
output_value_max: 180_000_000,
total_output_value: 199_995_000,
block_price_usd: Some(60_000.0),
..TxFeatures::default()
};
let registry = HeuristicRegistry::default_registry(0.7); // coinjoin threshold
let matches = registry.evaluate_all(&features);
for m in &matches {
println!("[{}] {} — {}", m.event_type, m.pattern, m.summary);
}
```
## Custom Heuristic
```rust
use bitcoin_heuristics::{Heuristic, HeuristicMatch, HeuristicStatus, TxFeatures};
use evidence_chain::EvidenceChain;
use serde_json::json;
struct MyHeuristic;
impl Heuristic for MyHeuristic {
fn id(&self) -> &'static str { "my-heuristic-v1" }
fn version(&self) -> &'static str { "1.0.0" }
fn status(&self) -> HeuristicStatus { HeuristicStatus::Experimental }
fn evaluate(&self, f: &TxFeatures) -> Option<HeuristicMatch> {
if f.output_count > 100 {
Some(HeuristicMatch::new(
self.id(), self.version(),
"high_fan_out", "structural", self.trigger_scope(),
format!("{} outputs detected", f.output_count),
serde_json::json!({ "output_count": f.output_count }),
))
} else {
None
}
}
fn build_evidence(&self, _: &TxFeatures) -> Option<EvidenceChain> { None }
}
```
## Long-Term Supply Activation (RPC)
The `LongTermSupplyActivationHeuristic` requires Bitcoin RPC access to fetch UTXO ages:
```rust
use bitcoin_heuristics::{JsonRpcClient, LongTermSupplyActivationHeuristic};
let rpc = JsonRpcClient::new("http://127.0.0.1:8332", "user", "pass");
// Second param: minimum UTXO age in blocks (~3 years = 157_680 blocks)
let h = LongTermSupplyActivationHeuristic::new(rpc, 157_680);
```
## Heuristics Reference
| `institutional-consolidation-v1` | tx | ≥10 inputs → ≤3 outputs, >$100k USD, homogeneous scripts |
| `institutional-distribution-v1` | tx | ≤3 inputs → ≥20 outputs, >$50k USD, homogeneous outputs |
| `coinjoin-detection-v1` | tx | CoinJoin score ≥ threshold, ≥5 inputs/outputs, equal outputs |
| `urgent-execution-v1` | tx | Fee rate spike above configured threshold |
| `address-reuse-detection-v1` | tx | ≥60% address reuse ratio, ≥2 reused, ≥3 outputs |
| `round-value-v1` | tx | Round output values, possible OTC / P2P trade signal |
| `hft-consolidation-v1` | block | ≥100 fresh inputs (≤7 days), ≤3 outputs |
| `long-term-supply-activation-v1` | tx | Dormant UTXOs (≥3 years) spent, >$50k USD (requires RPC) |
## License
MIT — see [LICENSE](LICENSE).