Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 178 additions & 0 deletions examples/order_book/05_verify_pnl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
"""
Verification Script: Portfolio P&L Tracking

1. Creates a new market (settles in 5 minutes).
2. Performs trades using different wallets (Market Maker, Buyer Taker).
3. Waits for settlement.
4. Verifies the Indexer P&L and Charting endpoints.
"""

import os
import time
import requests
from datetime import datetime, timezone, timedelta
from trufnetwork_sdk_py.client import TNClient

# Configuration
NODE_URL = "http://ec2-3-141-77-16.us-east-2.compute.amazonaws.com:8484"
INDEXER_URL = "http://ec2-52-15-66-172.us-east-2.compute.amazonaws.com:8080"

# Wallets
## WARNING: These are throwaway private keys provided for testnet examples only.
## DO NOT use these keys for production or store any real funds in these wallets.
MARKET_CREATOR_KEY = "a537437df2ed8d3bcb3b99b4f88818cadf8ac365cd0a66595bb50973ac4ecf51"
MARKET_MAKER_KEY = "1b94f77f8eeb3ff78aa091b0965bf1b54305e3af50f9a6cd24cb457edc8c77ed"
MARKET_MAKER_ADDR = "0xc11Ff6d3cC60823EcDCAB1089F1A4336053851EF"
BUYER_TAKER_KEY = "9b70937b21176cfa48f0859f4063c66a7998964cc2dfde873ef3d54c8fe04d74"
BUYER_TAKER_ADDR = "0x1c6790935a3a1A6B914399Ba743BEC8C41Fe89Fb"

# Market Parameters
BITCOIN_STREAM_ID = "st9058219c3c3247faf2b0a738de7027"
DATA_PROVIDER = "0xe5252596672cd0208a881bdb67c9df429916ba92"
THRESHOLD = "10000" # Guaranteed to be above (BTC price is high)
BRIDGE = "hoodi_tt2"

def with_retry(fn, *args, max_retries=5, initial_backoff=1, **kwargs):
"""
Executes a function with exponential backoff on failure.
"""
retries = 0
while retries < max_retries:
try:
return fn(*args, **kwargs)
except Exception as e:
retries += 1
if retries >= max_retries:
raise

backoff = initial_backoff * (2 ** (retries - 1))
print(f" ⚠️ Operation failed ({e}). Retrying in {backoff}s... ({retries}/{max_retries})")
time.sleep(backoff)

def main():
print("=" * 60)
print("Portfolio P&L Verification Script")
print("=" * 60)

# 1. Create Market
client_creator = TNClient(NODE_URL, MARKET_CREATOR_KEY)

# Settle in 5 minutes
now = datetime.now(timezone.utc)
settle_time = now + timedelta(minutes=5)
settle_timestamp = int(settle_time.timestamp())

print(f"\n1. Creating market settling at {settle_time.strftime('%H:%M:%S UTC')}...")
try:
with_retry(client_creator.create_price_above_threshold_market,
data_provider=DATA_PROVIDER,
stream_id=BITCOIN_STREAM_ID,
timestamp=settle_timestamp,
threshold=THRESHOLD,
bridge=BRIDGE,
settle_time=settle_timestamp,
max_spread=10,
min_order_size=1_000_000_000_000_000_000,
)
except Exception as e:
print(f"Error creating market: {e}")
return

time.sleep(5) # Wait for indexer to pick up the market
markets = with_retry(client_creator.list_markets, limit=10)
query_id = None
for m in markets:
if m.get('settle_time') == settle_timestamp:
query_id = m.get('id')
break

if not query_id:
print("Could not find created market query_id")
return
print(f"✓ Market Created: Query ID {query_id}")

# 2. Market Maker: Create YES holdings via split order
print(f"\n2. Market Maker ({MARKET_MAKER_ADDR[:10]}...) placing split order...")
client_mm = TNClient(NODE_URL, MARKET_MAKER_KEY)
# MM locks 10 tokens (10 YES, 10 NO). Collateral change should be -10.
with_retry(client_mm.place_split_limit_order, query_id, true_price=50, amount=10)
print("✓ MM Split order placed (-10 Collateral impact)")

# 3. Buyer Taker: Buy 5 YES from MM
print(f"\n3. Buyer Taker ({BUYER_TAKER_ADDR[:10]}...) buying 5 YES from MM...")
client_taker = TNClient(NODE_URL, BUYER_TAKER_KEY)
# Buy 5 YES at 50c = -2.5 collateral.
with_retry(client_taker.place_buy_order, query_id, outcome=True, price=50, amount=5)
print("✓ Buyer Taker matched MM (-2.5 Collateral impact)")
# MM should gain 2.5 collateral from the sell.

# 4. Wait for settlement
print(f"\n4. Waiting for settlement (approx {settle_time.strftime('%H:%M:%S UTC')})...")
while True:
now = datetime.now(timezone.utc)
if now > settle_time + timedelta(seconds=30):
break
remaining = (settle_time + timedelta(seconds=30) - now).total_seconds()
print(f" Waiting... {int(remaining)}s left", end="\r")
time.sleep(10)
print("\n✓ Market should be settled now.")

# 5. Verify Indexer P&L
print("\n5. Verifying LP Reward Distribution...")
dist_summary = with_retry(client_creator.get_distribution_summary, query_id)
if dist_summary:
print(f" Distribution Summary for Market {query_id}:")
print(f" Total Fees: {dist_summary.get('total_fees_distributed')}")
print(f" Distributed At: {dist_summary.get('distributed_at')}")
else:
print(f" No distribution summary found for market {query_id}")

print("\n6. Verifying Indexer Endpoints...")
time.sleep(10) # Wait for final sync cycle

for label, wallet in [("Market Maker", MARKET_MAKER_ADDR), ("Buyer Taker", BUYER_TAKER_ADDR)]:
print(f"\n--- {label} ({wallet}) ---")

# Check P&L Summary
pnl_url = f"{INDEXER_URL}/v0/prediction-market/participants/{wallet}/pnl"
resp = with_retry(requests.get, pnl_url)
if resp.status_code == 200:
data = resp.json().get("data", {})
print(" Summary P&L:")
print(f" Realized: {data.get('realized')}")
print(f" Unrealized: {data.get('unrealized')}")
print(f" Total: {data.get('total')}")
else:
print(f" Failed to get P&L summary: {resp.status_code}")

# Check Chart
chart_url = f"{INDEXER_URL}/v0/prediction-market/participants/{wallet}/chart"
resp = with_retry(requests.get, chart_url)
if resp.status_code == 200:
data = resp.json().get("data", [])
print(f" Chart points: {len(data)}")
if len(data) > 0:
latest = data[-1]
print(f" Latest Snapshot ({datetime.fromtimestamp(latest['timestamp'], timezone.utc).strftime('%H:%M:%S')}):")
print(f" Realized: {latest.get('realized')}")
print(f" Total: {latest.get('total')}")
else:
print(f" Failed to get Chart: {resp.status_code}")

# Check Rewards History
rewards_url = f"{INDEXER_URL}/v0/prediction-market/participants/{wallet}/rewards"
resp = with_retry(requests.get, rewards_url)
if resp.status_code == 200:
data = resp.json().get("data", {})
print(f" Reward History (Total: {data.get('total_rewards')}):")
for r in data.get("rewards", [])[:3]:
print(f" - Market {r.get('query_id')}: {r.get('reward_amount')} (at {r.get('distributed_at')})")
else:
print(f" Failed to get rewards history: {resp.status_code}")

print("\n" + "=" * 60)
print("Verification complete!")
print("=" * 60)

if __name__ == "__main__":
main()
105 changes: 105 additions & 0 deletions examples/order_book/06_check_lp_rewards.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python

import argparse
import time
from datetime import datetime, timezone
import requests

# --- Configuration ---
# Kwil/Node Configuration
INDEXER_URL = "http://ec2-52-15-66-172.us-east-2.compute.amazonaws.com:8080"

# Wallets
MARKET_MAKER_ADDR = "0xc11Ff6d3cC60823EcDCAB1089F1A4336053851EF"
BUYER_TAKER_ADDR = "0x1c6790935a3a1A6B914399Ba743BEC8C41Fe89Fb"

# --- Helper Functions ---
def with_retry(fn, *args, max_retries=5, initial_backoff=1, **kwargs):
"""
Executes a function with exponential backoff on failure.
Returns None if all retries fail.
"""
retries = 0
err_msg = ""
while retries < max_retries:
try:
resp = fn(*args, **kwargs)
if hasattr(resp, "status_code") and resp.status_code == 200:
return resp

err_msg = f"Status {resp.status_code}" if hasattr(resp, "status_code") else "Unknown failure"
except Exception as e:
err_msg = str(e)

retries += 1
if retries >= max_retries:
print(f" ❌ All {max_retries} attempts failed: {err_msg}")
return None

backoff = initial_backoff * (2 ** (retries - 1))
print(f" ⚠️ Attempt {retries} failed ({err_msg}). Retrying in {backoff}s... ({retries}/{max_retries})")
time.sleep(backoff)

def main(query_id: int):
"""
Checks the LP reward distribution status for a given market.
"""
print("=" * 60)
print("LP Reward Distribution Verification Script")
print(f"Market Query ID: {query_id}")
print("=" * 60)

# 1. Verify Distribution Summary
dist_url = f"{INDEXER_URL}/v0/prediction-market/markets/{query_id}/distribution"
resp = with_retry(requests.get, dist_url)
if resp and resp.status_code == 200:
data = resp.json().get("data", {})
print(f"1. Distribution Summary for Market {query_id}:")
print(f" - Total Fees Distributed: {data.get('total_fees_distributed')}")

dist_at = data.get('distributed_at', 0)
if dist_at > 0:
dist_time = datetime.fromtimestamp(dist_at, timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')
print(f" - Distributed At: {dist_time}")
else:
print(" - Distributed At: Not yet distributed")

print(f" - Total LP Count: {data.get('total_lp_count')}")
print(f" - Data Provider Fees: {data.get('total_dp_fees')}")
print(f" - Validator Fees: {data.get('total_validator_fees')}")
else:
print(f"1. Failed to get distribution summary (Status 404 or Timeout)")

# 2. Check Reward History for known participants
print("\n2. Participant Reward History:")
for label, wallet in [("Market Maker", MARKET_MAKER_ADDR), ("Buyer Taker", BUYER_TAKER_ADDR)]:
rewards_url = f"{INDEXER_URL}/v0/prediction-market/participants/{wallet}/rewards?query_id={query_id}"
resp = with_retry(requests.get, rewards_url)
if resp and resp.status_code == 200:
data = resp.json().get("data", {})
rewards = data.get("rewards", [])
print(f" - {label} ({wallet[:10]}...):")
if rewards:
for r in rewards:
reward_amount = r.get('reward_amount', 0)
ts = r.get('distributed_at')
if ts is not None and isinstance(ts, (int, float)):
dist_time = datetime.fromtimestamp(ts, timezone.utc).strftime('%H:%M:%S')
else:
dist_time = "unknown time"
print(f" - Earned: {reward_amount} (at {dist_time})")
else:
print(" - No rewards found for this market.")
else:
print(f" - {label}: Failed to get rewards history.")

print("" + "=" * 60)
print("Verification complete!")
print("=" * 60)

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Check LP reward distribution for a given market.")
parser.add_argument("query_id", type=int, nargs="?", default=14164, help="The Query ID of the market to check (defaults to 14164).")
args = parser.parse_args()

main(args.query_id)
Loading