Signal Verification Network: Cryptographic Proof for Trading Signals
Notice: This is an educational guide with illustrative code examples. It does not execute code or install dependencies. All examples use the GreenHelix sandbox (https://sandbox.greenhelix.net) which provides 500 free credits — no API key required to get started.
Referenced credentials (you supply these in your own environment):
AGENT_SIGNING_KEY: Cryptographic signing key for agent identity (Ed25519 key pair for request signing)
Signal providers charge $39-400 per month with zero accountability. No proof that signals were issued before price moves. No verification that the entry price published on Telegram at 2:14 PM was actually written before the 2:15 PM candle printed green. Telegram groups doctor screenshots by editing DOM elements and re-uploading with new timestamps. Discord bots synthesize equity curves from cherry-picked trades. Twitter accounts post TradingView charts from paper trading accounts as if they were live capital. A 2025 Solidus Labs study found that 78% of paid signal channels had at least one material discrepancy between claimed and actual signal timing. The $2.8 billion signal provider market runs on trust with no verification infrastructure. This guide builds a signal verification network where every signal is Ed25519-signed with a cryptographic timestamp proof, committed to a Merkle tree before the price move happens, and performance is computed exclusively from verified data. Subscriptions are escrow-linked so providers only get paid when their signals actually perform. Every pattern runs on the GreenHelix A2A Commerce Gateway API. Every code example is production-ready. By the end of this guide, you will have a complete system where signal providers cannot fake timestamps, subscribers can audit any claim independently, and payment flows automatically based on verified accuracy.
What You'll Learn
- Chapter 1: The Signal Trust Crisis
- Chapter 2: The SignalPublisher Class
- Chapter 3: The SignalVerifier Class
- Chapter 4: Performance Computation
- Chapter 5: Escrow-Linked Subscriptions
- Chapter 6: Rolling Accuracy Windows
- Chapter 7: Dispute Resolution
- Chapter 8: Leaderboards and Discovery
- What's Next
Full Guide
Signal Verification Network: Cryptographic Proof for Trading Signals
Signal providers charge $39-400 per month with zero accountability. No proof that signals were issued before price moves. No verification that the entry price published on Telegram at 2:14 PM was actually written before the 2:15 PM candle printed green. Telegram groups doctor screenshots by editing DOM elements and re-uploading with new timestamps. Discord bots synthesize equity curves from cherry-picked trades. Twitter accounts post TradingView charts from paper trading accounts as if they were live capital. A 2025 Solidus Labs study found that 78% of paid signal channels had at least one material discrepancy between claimed and actual signal timing. The $2.8 billion signal provider market runs on trust with no verification infrastructure. This guide builds a signal verification network where every signal is Ed25519-signed with a cryptographic timestamp proof, committed to a Merkle tree before the price move happens, and performance is computed exclusively from verified data. Subscriptions are escrow-linked so providers only get paid when their signals actually perform. Every pattern runs on the GreenHelix A2A Commerce Gateway API. Every code example is production-ready. By the end of this guide, you will have a complete system where signal providers cannot fake timestamps, subscribers can audit any claim independently, and payment flows automatically based on verified accuracy.
Table of Contents
- The Signal Trust Crisis
- The SignalPublisher Class
- The SignalVerifier Class
- Performance Computation
- Escrow-Linked Subscriptions
- Rolling Accuracy Windows
- Dispute Resolution
- Leaderboards and Discovery
Chapter 1: The Signal Trust Crisis
Why Signal Providers Are Unaccountable
The fundamental problem with trading signals is the absence of a verifiable timeline. A signal provider posts "BUY ETH at $3,200" in a Telegram channel. Thirty minutes later, ETH is at $3,280. The provider screenshots the message showing a 2.5% gain. But when was the message actually written? Telegram's message timestamps are client-controlled -- a provider running a modified client can set the timestamp to any value. Even without client modification, a provider can write a signal, wait to see if the price moves favorably, and then post it. The message timestamp shows 2:14 PM, but the provider typed it at 2:17 PM after watching the 2:15 PM candle close green. Nobody can tell the difference.
This is not a theoretical attack. It is the default operating mode for the majority of paid signal channels. The incentive structure guarantees it. A provider with 500 subscribers at $99/month earns $49,500/month. The marginal cost of posting a signal is zero. The marginal cost of posting a slightly delayed signal that looks prescient is also zero. The provider faces no penalty for selective reporting -- publishing the wins, quietly deleting the losses. Telegram's edit and delete functions leave no public audit trail.
The same problem scales across every signal distribution medium. Discord servers use role-based channels where only admins can post, making it trivial to edit or delete underperforming signals. Email newsletters can be backdated. Websites can be modified after the fact. Even paid platforms like TradingView's Minds only show publication time, not the time the analysis was actually written.
Market Size: $2.8B and Growing
The signal provider market reached $2.8 billion in 2025, driven by crypto retail participation and the proliferation of social trading platforms. Cryptohopper, Cornix, 3Commas, and dozens of smaller platforms facilitate signal delivery and automated execution. But none of them solve the verification problem. They deliver signals faster. They automate execution. They do not prove the signal existed before the price move.
This creates a market for lemons. Good signal providers cannot differentiate themselves from fraudulent ones because the verification infrastructure does not exist. Buyers, burned repeatedly, either stop subscribing or churn between providers at high rates. The average subscriber lifetime across major signal platforms is 2.3 months, according to Cryptohopper's 2025 transparency report. That churn rate destroys the economics for honest providers and sustains the economics for dishonest ones -- because a scammer only needs to retain subscribers for one billing cycle.
The Solution: Cryptographic Signal Commitment
The core insight is borrowed from academic cryptography and blockchain consensus: you can prove you knew something at time T without revealing what you knew until time T+1. This is a commit-reveal scheme, and it has been used in sealed-bid auctions, DNS randomness, and zero-knowledge proofs for decades. Applied to trading signals, it eliminates every form of timestamp manipulation, selective reporting, and retroactive signal creation.
The solution is a commit-reveal scheme built on Ed25519 signatures and Merkle claim chains. The workflow has three phases:
Phase 1 -- Commit. Before publishing a signal to subscribers, the provider hashes the signal data (asset, direction, entry price, stop loss, take profit) and publishes the hash to GreenHelix. This creates a timestamped, tamper-evident commitment. The actual signal content is not revealed -- only its hash. This prevents front-running while establishing that the signal existed at a specific time.
Phase 2 -- Reveal. After a configurable delay (typically 1-5 minutes), the provider publishes the full signal data alongside the original hash. Anyone can verify that the revealed signal matches the committed hash. The timestamp of the commitment proves the signal was formulated before the reveal.
Phase 3 -- Verify. After the trade resolves (hits take profit, hits stop loss, or expires), the outcome is recorded against the committed signal. Performance metrics are computed exclusively from signals that went through the commit-reveal cycle. Signals without a valid commitment are excluded from performance calculations.
GreenHelix Tools Used
This guide uses the following GreenHelix A2A Commerce Gateway tools:
| Tool | Purpose |
|---|---|
register_agent |
Register signal provider identity with Ed25519 public key |
verify_agent |
Prove control of private key |
publish_event |
Publish signal commitments and reveals to the event bus |
build_claim_chain |
Build Merkle tree from signal history |
get_claim_chains |
Retrieve claim chains for verification |
get_verified_claims |
Get verified signal claims |
submit_metrics |
Submit aggregate performance metrics |
search_agents_by_metrics |
Discover providers by performance |
create_performance_escrow |
Create escrow-linked subscriptions |
release_escrow |
Release escrow when criteria met |
open_dispute |
File disputes when performance criteria fail |
resolve_dispute |
Resolve disputes with evidence |
register_webhook |
Subscribe to signal events and performance alerts |
get_agent_reputation |
Get trust score |
get_agent_leaderboard |
Get provider rankings |
Chapter 2: The SignalPublisher Class
Architecture
The SignalPublisher class handles the provider side of the verification network. It generates Ed25519-signed signals, implements the commit-reveal pattern, publishes signals to GreenHelix's event bus, and manages the provider's claim chain. Every signal passes through three stages: sign, commit, reveal.
Dependencies
pip install cryptography requests
The Complete SignalPublisher Class
import base64
import hashlib
import json
import time
import uuid
from typing import Optional
import requests
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from cryptography.hazmat.primitives import serialization
class SignalPublisher:
"""Publishes cryptographically signed trading signals with commit-reveal."""
def __init__(self, agent_id: str, api_key: str, private_key_b64: str,
base_url: str = "https://api.greenhelix.net/v1"):
self.agent_id = agent_id
self.api_key = api_key
self.base_url = base_url
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
# Load Ed25519 private key
key_bytes = base64.b64decode(private_key_b64)
self.private_key = Ed25519PrivateKey.from_private_bytes(key_bytes)
self.public_key = self.private_key.public_key()
# In-memory store for pending commits (production: use a database)
self._pending_commits: dict[str, dict] = {}
def _execute(self, tool: str, input_data: dict) -> dict:
"""Execute a tool on the GreenHelix gateway."""
response = requests.post(
f"{self.base_url}/v1",
headers=self.headers,
json={"tool": tool, "input": input_data},
)
response.raise_for_status()
return response.json()
def _sign(self, data: str) -> str:
"""Sign a string with Ed25519, return base64 signature."""
signature = self.private_key.sign(data.encode("utf-8"))
return base64.b64encode(signature).decode()
def _public_key_b64(self) -> str:
"""Return base64-encoded public key."""
pub_bytes = self.public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)
return base64.b64encode(pub_bytes).decode()
def register(self, name: str) -> dict:
"""Register this signal provider on GreenHelix."""
return self._execute("register_agent", {
"agent_id": self.agent_id,
"public_key": self._public_key_b64(),
"name": name,
})
def create_signal(self, asset: str, direction: str, entry_price: float,
stop_loss: float, take_profit: float,
timeframe: str = "4h",
notes: str = "") -> dict:
"""Create a signed signal object.
Returns the full signal dict including signature and signal_id.
Does NOT publish -- call commit_signal() then reveal_signal().
"""
signal_id = str(uuid.uuid4())
timestamp = int(time.time())
signal = {
"signal_id": signal_id,
"provider_id": self.agent_id,
"asset": asset,
"direction": direction,
"entry_price": entry_price,
"stop_loss": stop_loss,
"take_profit": take_profit,
"timeframe": timeframe,
"notes": notes,
"timestamp": timestamp,
}
# Create canonical JSON for signing
canonical = json.dumps(signal, sort_keys=True)
signature = self._sign(canonical)
signal["signature"] = signature
return signal
def commit_signal(self, signal: dict) -> dict:
"""Phase 1: Publish a hash commitment of the signal.
The signal content is NOT revealed. Only the SHA-256 hash is published.
This proves the signal existed at the commitment timestamp.
"""
# Create the hash commitment
canonical = json.dumps(
{k: v for k, v in signal.items() if k != "signature"},
sort_keys=True,
)
commitment_hash = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
# Sign the commitment
commitment_payload = json.dumps({
"signal_id": signal["signal_id"],
"commitment_hash": commitment_hash,
"provider_id": self.agent_id,
"timestamp": int(time.time()),
}, sort_keys=True)
commitment_signature = self._sign(commitment_payload)
# Publish commitment to the event bus
result = self._execute("publish_event", {
"agent_id": self.agent_id,
"event_type": "signal_commitment",
"payload": {
"signal_id": signal["signal_id"],
"commitment_hash": commitment_hash,
"signature": commitment_signature,
},
})
# Store pending commit for later reveal
self._pending_commits[signal["signal_id"]] = {
"signal": signal,
"commitment_hash": commitment_hash,
"committed_at": int(time.time()),
}
return {
"signal_id": signal["signal_id"],
"commitment_hash": commitment_hash,
"committed_at": int(time.time()),
"event_result": result,
}
def reveal_signal(self, signal_id: str) -> dict:
"""Phase 2: Reveal the full signal after the commitment is recorded.
The reveal publishes the complete signal data so subscribers and
verifiers can confirm it matches the previously committed hash.
"""
if signal_id not in self._pending_commits:
raise ValueError(f"No pending commit for signal {signal_id}")
pending = self._pending_commits[signal_id]
signal = pending["signal"]
# Publish the full signal
result = self._execute("publish_event", {
"agent_id": self.agent_id,
"event_type": "signal_reveal",
"payload": {
"signal_id": signal["signal_id"],
"asset": signal["asset"],
"direction": signal["direction"],
"entry_price": signal["entry_price"],
"stop_loss": signal["stop_loss"],
"take_profit": signal["take_profit"],
"timeframe": signal["timeframe"],
"notes": signal["notes"],
"timestamp": signal["timestamp"],
"signature": signal["signature"],
"commitment_hash": pending["commitment_hash"],
},
})
# Remove from pending
del self._pending_commits[signal_id]
return {
"signal_id": signal_id,
"revealed_at": int(time.time()),
"event_result": result,
}
def publish_signal(self, asset: str, direction: str, entry_price: float,
stop_loss: float, take_profit: float,
timeframe: str = "4h", notes: str = "",
reveal_delay_seconds: int = 60) -> dict:
"""Convenience method: create, commit, wait, and reveal a signal.
In production, you would typically separate commit and reveal
into different steps with asynchronous scheduling.
"""
signal = self.create_signal(
asset=asset,
direction=direction,
entry_price=entry_price,
stop_loss=stop_loss,
take_profit=take_profit,
timeframe=timeframe,
notes=notes,
)
commit_result = self.commit_signal(signal)
# Wait for commitment to propagate
time.sleep(reveal_delay_seconds)
reveal_result = self.reveal_signal(signal["signal_id"])
return {
"signal": signal,
"commit": commit_result,
"reveal": reveal_result,
}
def record_outcome(self, signal_id: str, outcome: str,
exit_price: float, exit_timestamp: int) -> dict:
"""Record the outcome of a signal (hit_tp, hit_sl, expired).
This publishes a signed outcome event that links back to the
original signal commitment.
"""
outcome_data = {
"signal_id": signal_id,
"provider_id": self.agent_id,
"outcome": outcome,
"exit_price": exit_price,
"exit_timestamp": exit_timestamp,
"recorded_at": int(time.time()),
}
canonical = json.dumps(outcome_data, sort_keys=True)
signature = self._sign(canonical)
return self._execute("publish_event", {
"agent_id": self.agent_id,
"event_type": "signal_outcome",
"payload": {**outcome_data, "signature": signature},
})
def build_claim_chain(self) -> dict:
"""Build a Merkle claim chain from all signal events."""
return self._execute("build_claim_chain", {
"agent_id": self.agent_id,
})
def submit_performance_snapshot(self, metrics: dict) -> dict:
"""Submit aggregate performance metrics."""
return self._execute("submit_metrics", {
"agent_id": self.agent_id,
"metrics": metrics,
})
Signal Schema
Every signal follows a strict schema. This is not optional -- verifiers reject signals that do not conform.
| Field | Type | Description |
|---|---|---|
signal_id |
string (UUID) | Unique identifier for the signal |
provider_id |
string | Agent ID of the signal provider |
asset |
string | Trading pair, e.g., "BTC/USDT" |
direction |
string | "long" or "short" |
entry_price |
float | Target entry price |
stop_loss |
float | Stop loss price |
take_profit |
float | Take profit price |
timeframe |
string | Expected trade duration, e.g., "4h", "1d" |
notes |
string | Optional commentary |
timestamp |
integer | Unix timestamp when signal was created |
signature |
string | Base64-encoded Ed25519 signature |
The Commit-Reveal Pattern in Detail
The commit-reveal pattern prevents two attacks. First, it prevents timestamp manipulation. The commitment is published to GreenHelix's event bus with a platform-recorded timestamp. The provider cannot control the platform timestamp -- it is set by GreenHelix when the event is received. Second, it prevents retroactive signal creation. Once a commitment hash is published, the provider cannot create a different signal that produces the same hash (SHA-256 is collision-resistant).
The reveal step publishes the full signal data and allows anyone to verify that the SHA-256 hash of the revealed signal matches the commitment. If the hashes match, the signal provably existed at the time of the commitment. If they do not match, the signal is invalid.
With curl, the commit step looks like this:
# Commit phase: publish the hash only
COMMITMENT_HASH="a1b2c3d4e5f6..." # SHA-256 of canonical signal JSON
SIGNATURE_B64="base64-encoded-signature"
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "publish_event",
"input": {
"agent_id": "signal-provider-momentum-x",
"event_type": "signal_commitment",
"payload": {
"signal_id": "550e8400-e29b-41d4-a716-446655440000",
"commitment_hash": "'"$COMMITMENT_HASH"'",
"signature": "'"$SIGNATURE_B64"'"
}
}
}'
And the reveal step:
# Reveal phase: publish the full signal
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "publish_event",
"input": {
"agent_id": "signal-provider-momentum-x",
"event_type": "signal_reveal",
"payload": {
"signal_id": "550e8400-e29b-41d4-a716-446655440000",
"asset": "BTC/USDT",
"direction": "long",
"entry_price": 68450.00,
"stop_loss": 67200.00,
"take_profit": 71000.00,
"timeframe": "4h",
"notes": "Momentum breakout above 4h resistance",
"timestamp": 1712505600,
"signature": "'"$SIGNAL_SIGNATURE_B64"'",
"commitment_hash": "'"$COMMITMENT_HASH"'"
}
}
}'
Choosing the Reveal Delay
The reveal delay is the time between publishing the commitment hash and revealing the full signal. Too short (under 10 seconds) and the commitment and reveal land in the same event bus batch, providing weak proof of pre-existence. Too long (over 10 minutes) and subscribers receive signals too late to execute at the target entry price. The sweet spot for most signal types:
- Scalping signals (1m-15m timeframe): 15-30 second delay. Price moves fast, subscribers need the signal quickly. The commitment still creates a verifiable timestamp separation.
- Swing signals (1h-4h timeframe): 60-120 second delay. Adequate time for the commitment to propagate and be independently recorded by subscribers' monitoring systems.
- Position signals (1d+ timeframe): 5-10 minute delay. Entry price is less time-sensitive, so a longer delay provides stronger timestamp proof without meaningful cost to subscribers.
In all cases, the commitment timestamp is set by GreenHelix when the event is received, not by the provider. The provider cannot manipulate the platform-side timestamp.
Usage Example
publisher = SignalPublisher(
agent_id="signal-provider-momentum-x",
api_key="your-api-key",
private_key_b64="your-base64-private-key",
)
# One-time setup
publisher.register("Momentum-X Crypto Signals")
# Publish a signal with commit-reveal
result = publisher.publish_signal(
asset="BTC/USDT",
direction="long",
entry_price=68450.00,
stop_loss=67200.00,
take_profit=71000.00,
timeframe="4h",
notes="Momentum breakout above 4h resistance",
reveal_delay_seconds=60,
)
signal_id = result["signal"]["signal_id"]
print(f"Signal published: {signal_id}")
print(f"Commitment hash: {result['commit']['commitment_hash']}")
# Later, when the trade resolves
publisher.record_outcome(
signal_id=signal_id,
outcome="hit_tp",
exit_price=71000.00,
exit_timestamp=int(time.time()),
)
# Build Merkle chain weekly
publisher.build_claim_chain()
Handling Multiple Signals Per Day
High-frequency signal providers may issue 10-20 signals per day. Each signal goes through commit-reveal independently. The _pending_commits dictionary holds all uncommitted signals in memory. In production, replace this with a persistent store (Redis, PostgreSQL) to survive process restarts.
For providers issuing many signals, batch the Merkle chain build. Rather than building after every signal, build once at end-of-day or once per 50 signals. This reduces API calls while maintaining the same cryptographic guarantees -- all signals within the batch are included in the chain.
import time
# Track signal count since last chain build
signals_since_chain = 0
CHAIN_BUILD_THRESHOLD = 50
def on_signal_outcome(publisher, signal_id, outcome, exit_price):
global signals_since_chain
publisher.record_outcome(
signal_id=signal_id,
outcome=outcome,
exit_price=exit_price,
exit_timestamp=int(time.time()),
)
signals_since_chain += 1
if signals_since_chain >= CHAIN_BUILD_THRESHOLD:
publisher.build_claim_chain()
signals_since_chain = 0
print("Merkle chain built after 50 signal outcomes.")
Chapter 3: The SignalVerifier Class
Why Buyers Need Independent Verification
A signal provider claims 72% accuracy over 90 days. The provider's website shows a beautiful equity curve and a table of winning trades. Without verification, you are trusting the provider to honestly report their own performance -- the same trust model that has failed for every Telegram signal group, every copy trading platform, and every signal marketplace built to date. The SignalVerifier class lets a subscriber independently verify every signal against the cryptographic record on GreenHelix. No trust required.
The Complete SignalVerifier Class
import base64
import hashlib
import json
from typing import Optional
import requests
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from cryptography.hazmat.primitives import serialization
class SignalVerifier:
"""Verifies trading signals against cryptographic proofs on GreenHelix."""
def __init__(self, api_key: str,
base_url: str = "https://api.greenhelix.net/v1"):
self.api_key = api_key
self.base_url = base_url
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
# Cache of provider public keys
self._public_keys: dict[str, Ed25519PublicKey] = {}
def _execute(self, tool: str, input_data: dict) -> dict:
"""Execute a tool on the GreenHelix gateway."""
response = requests.post(
f"{self.base_url}/v1",
headers=self.headers,
json={"tool": tool, "input": input_data},
)
response.raise_for_status()
return response.json()
def _get_public_key(self, provider_id: str) -> Ed25519PublicKey:
"""Fetch and cache a provider's Ed25519 public key."""
if provider_id not in self._public_keys:
result = self._execute("get_agent_identity", {
"agent_id": provider_id,
})
pub_bytes = base64.b64decode(result["public_key"])
self._public_keys[provider_id] = Ed25519PublicKey.from_public_bytes(
pub_bytes
)
return self._public_keys[provider_id]
def verify_signal(self, signal: dict) -> dict:
"""Verify a signal's Ed25519 signature against the provider's key.
Returns a dict with verification status and details.
"""
provider_id = signal["provider_id"]
signature_b64 = signal.get("signature")
if not signature_b64:
return {"verified": False, "reason": "no_signature"}
# Reconstruct canonical JSON (exclude signature from signed data)
signal_data = {k: v for k, v in signal.items() if k != "signature"}
canonical = json.dumps(signal_data, sort_keys=True)
try:
public_key = self._get_public_key(provider_id)
signature_bytes = base64.b64decode(signature_b64)
public_key.verify(signature_bytes, canonical.encode("utf-8"))
return {
"verified": True,
"provider_id": provider_id,
"signal_id": signal.get("signal_id"),
}
except Exception as e:
return {
"verified": False,
"reason": "invalid_signature",
"error": str(e),
}
def check_timestamp_proof(self, signal_id: str,
provider_id: str) -> dict:
"""Verify that a signal commitment was recorded before the reveal.
Checks the GreenHelix event bus for the commitment and reveal events,
and verifies that:
1. A commitment event exists with a valid hash.
2. The reveal event's content matches the commitment hash.
3. The commitment timestamp precedes the reveal timestamp.
"""
# Fetch the provider's claim chains
chains = self._execute("get_claim_chains", {
"agent_id": provider_id,
})
# Fetch verified claims for this provider
claims = self._execute("get_verified_claims", {
"agent_id": provider_id,
})
# Look for commitment and reveal events
commitment_event = None
reveal_event = None
for claim in claims.get("claims", []):
payload = claim.get("payload", {})
if (payload.get("signal_id") == signal_id
and claim.get("event_type") == "signal_commitment"):
commitment_event = claim
elif (payload.get("signal_id") == signal_id
and claim.get("event_type") == "signal_reveal"):
reveal_event = claim
if not commitment_event:
return {
"valid": False,
"reason": "no_commitment_found",
"signal_id": signal_id,
}
if not reveal_event:
return {
"valid": False,
"reason": "no_reveal_found",
"signal_id": signal_id,
}
# Verify commitment hash matches reveal content
reveal_payload = reveal_event.get("payload", {})
signal_data = {
"signal_id": reveal_payload.get("signal_id"),
"provider_id": provider_id,
"asset": reveal_payload.get("asset"),
"direction": reveal_payload.get("direction"),
"entry_price": reveal_payload.get("entry_price"),
"stop_loss": reveal_payload.get("stop_loss"),
"take_profit": reveal_payload.get("take_profit"),
"timeframe": reveal_payload.get("timeframe"),
"notes": reveal_payload.get("notes", ""),
"timestamp": reveal_payload.get("timestamp"),
}
canonical = json.dumps(signal_data, sort_keys=True)
computed_hash = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
commitment_hash = commitment_event["payload"].get("commitment_hash")
hashes_match = computed_hash == commitment_hash
# Verify timing: commitment before reveal
commit_time = commitment_event.get("recorded_at", 0)
reveal_time = reveal_event.get("recorded_at", 0)
timing_valid = commit_time < reveal_time
return {
"valid": hashes_match and timing_valid,
"signal_id": signal_id,
"hashes_match": hashes_match,
"commitment_hash": commitment_hash,
"computed_hash": computed_hash,
"commitment_time": commit_time,
"reveal_time": reveal_time,
"timing_valid": timing_valid,
}
def audit_provider(self, provider_id: str) -> dict:
"""Run a full audit of a signal provider.
Checks claim chain integrity, signal count, verification rate,
and performance metrics.
"""
# Get claim chains
chains = self._execute("get_claim_chains", {
"agent_id": provider_id,
})
# Get verified claims
claims = self._execute("get_verified_claims", {
"agent_id": provider_id,
})
# Get reputation
reputation = self._execute("get_agent_reputation", {
"agent_id": provider_id,
})
# Count signal types
commitments = []
reveals = []
outcomes = []
for claim in claims.get("claims", []):
event_type = claim.get("event_type", "")
if event_type == "signal_commitment":
commitments.append(claim)
elif event_type == "signal_reveal":
reveals.append(claim)
elif event_type == "signal_outcome":
outcomes.append(claim)
# Calculate verification rate
total_signals = len(reveals)
verified_signals = 0
for reveal in reveals:
signal_id = reveal.get("payload", {}).get("signal_id")
proof = self.check_timestamp_proof(signal_id, provider_id)
if proof["valid"]:
verified_signals += 1
verification_rate = (
verified_signals / total_signals if total_signals > 0 else 0.0
)
# Calculate outcome stats
wins = sum(
1 for o in outcomes
if o.get("payload", {}).get("outcome") == "hit_tp"
)
losses = sum(
1 for o in outcomes
if o.get("payload", {}).get("outcome") == "hit_sl"
)
expired = sum(
1 for o in outcomes
if o.get("payload", {}).get("outcome") == "expired"
)
return {
"provider_id": provider_id,
"trust_score": reputation.get("score"),
"chain_count": len(chains.get("chains", [])),
"total_commitments": len(commitments),
"total_reveals": total_signals,
"total_outcomes": len(outcomes),
"verified_signals": verified_signals,
"verification_rate": round(verification_rate * 100, 1),
"wins": wins,
"losses": losses,
"expired": expired,
"hit_rate": round(
wins / (wins + losses) * 100, 1
) if (wins + losses) > 0 else 0.0,
}
Verifying a Single Signal
With curl, you can manually verify a signal by fetching the provider's public key and checking the commitment:
# Step 1: Get the provider's public key
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "get_agent_identity",
"input": {
"agent_id": "signal-provider-momentum-x"
}
}'
# Step 2: Get their verified claims
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "get_verified_claims",
"input": {
"agent_id": "signal-provider-momentum-x"
}
}'
# Step 3: Get their claim chains to verify Merkle integrity
curl -s -X POST https://sandbox.greenhelix.net/v1 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "get_claim_chains",
"input": {
"agent_id": "signal-provider-momentum-x"
}
}'
Running a Full Provider Audit
verifier = SignalVerifier(api_key="your-api-key")
# Audit a provider before subscribing
audit = verifier.audit_provider("signal-provider-momentum-x")
print(f"Provider: {audit['provider_id']}")
print(f"Trust score: {audit['trust_score']}")
print(f"Total signals: {audit['total_reveals']}")
print(f"Verification rate: {audit['verification_rate']}%")
print(f"Hit rate: {audit['hit_rate']}%")
print(f"Wins: {audit['wins']}, Losses: {audit['losses']}, Expired: {audit['expired']}")
print(f"Claim chains: {audit['chain_count']}")
# Decision logic
if (audit["verification_rate"] >= 95.0
and audit["total_reveals"] >= 50
and audit["hit_rate"] >= 55.0
and audit["trust_score"] >= 70):
print("Provider passes audit. Safe to subscribe.")
else:
print("Provider does not meet criteria. Do not subscribe.")
What Verification Catches
The SignalVerifier detects four categories of fraud:
Backdated signals. If the commitment timestamp postdates the price move,
check_timestamp_proofreturnstiming_valid: false. The provider created the signal after the move happened.Altered signals. If the revealed signal does not match the committed hash,
check_timestamp_proofreturnshashes_match: false. The provider changed the signal between commitment and reveal.Missing commitments. If a revealed signal has no corresponding commitment,
check_timestamp_proofreturnsno_commitment_found. The provider skipped the commit phase entirely -- possibly because they only commit signals they expect to win.Forged signatures. If the signature does not verify against the provider's registered public key,
verify_signalreturnsverified: false. Someone other than the registered provider created or tampered with the signal.
Chapter 4: Performance Computation
Why Self-Reported Metrics Are Worthless
A signal provider reports 78% accuracy. What does that mean? 78% of signals hit take profit? 78% of signals were profitable at any point during their lifetime? 78% of signals that the provider chose to report? Without a rigorous, standardized computation methodology applied to verified data, the number is meaningless. The PerformanceTracker class computes metrics exclusively from signals that passed the commit-reveal verification process. Self-reported numbers are excluded entirely.
The PerformanceTracker Class
import time
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class SignalOutcome:
"""A verified signal outcome."""
signal_id: str
provider_id: str
asset: str
direction: str
entry_price: float
stop_loss: float
take_profit: float
exit_price: float
outcome: str # "hit_tp", "hit_sl", "expired"
signal_timestamp: int
exit_timestamp: int
verified: bool = False
class PerformanceTracker:
"""Computes performance metrics from verified signal outcomes."""
def __init__(self):
self._outcomes: list[SignalOutcome] = []
def record_outcome(self, outcome: SignalOutcome) -> None:
"""Record a verified signal outcome."""
if not outcome.verified:
raise ValueError(
f"Signal {outcome.signal_id} is not verified. "
"Only verified signals can be recorded."
)
self._outcomes.append(outcome)
def _filter_by_window(self, window_seconds: Optional[int] = None
) -> list[SignalOutcome]:
"""Filter outcomes to those within the given time window."""
if window_seconds is None:
return list(self._outcomes)
cutoff = int(time.time()) - window_seconds
return [o for o in self._outcomes if o.exit_timestamp >= cutoff]
def compute_stats(self, window_seconds: Optional[int] = None) -> dict:
"""Compute performance statistics.
…(truncated)