bitsentry 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ .env
5
+ validation/*.json
6
+ *.db
7
+ dist/
8
+ build/
9
+ *.egg-info/
10
+ .venv/
11
+ .pytest_cache/
@@ -0,0 +1,43 @@
1
+ # BitSentry — AGENTS.md
2
+
3
+ ## What We Are Building
4
+ BitSentry is a pip-installable Python library (`pip install bitsentry`) that is the safety, audit, and intelligence layer for Bitget trading agents and traders. Built for the Bitget AI Base Camp Hackathon S1 — Track 2: Trading Infra.
5
+
6
+ ## Core Mission
7
+ Every Bitget trading agent is a black box. When it loses money, nobody knows why. BitSentry fixes that by wrapping every trade decision with risk enforcement, audit logging, and performance intelligence.
8
+
9
+ ## 5 Layers We Are Building
10
+ 1. BGCClient — wrapper around bgc CLI for all Bitget data calls (DONE)
11
+ 2. AuditEngine — SQLite logger + SHA-256 integrity verification (NEXT)
12
+ 3. RiskGuardian — 5-layer risk middleware that approves or blocks trades
13
+ 4. PositionMonitor — live position safety ratings (green/yellow/red)
14
+ 5. StrategyEvaluator — rolling win rate tracker with PERFORMING/DEGRADING/DEAD verdict
15
+
16
+ ## Plus
17
+ - FastAPI REST server exposing all layers as endpoints
18
+ - React dashboard for human traders
19
+ - MCP server for AI agent integration
20
+ - Makefile with: make install, make test, make validate, make verify
21
+
22
+ ## Credentials (Demo Account)
23
+ BITGET_DEMO_API_KEY=bg_a3a670745f27aaee1609acfe408215aa
24
+ BITGET_DEMO_SECRET_KEY=912bf1e07cf01cc21ad485954d202b0e72fcab89877131129fa793d427fb50e9
25
+ BITGET_DEMO_PASSPHRASE=BitgetDemoKey
26
+
27
+ ## Build Rules
28
+ - Always read existing files before editing them
29
+ - Never guess bgc tool names — run bgc --help first
30
+ - Test every file immediately after writing it
31
+ - One component at a time, confirm it works before moving on
32
+ - Demo mode: always use BGCClient(demo=True) in tests
33
+ - All bgc calls go through BGCClient, never call bgc directly in other files
34
+
35
+ ## Current Status
36
+ - bitsentry/bgc_client.py — COMPLETE AND TESTED
37
+ - bitsentry/audit_engine.py — IN PROGRESS
38
+ - bitsentry/risk_guardian.py — TODO
39
+ - bitsentry/position_monitor.py — TODO
40
+ - bitsentry/strategy_evaluator.py — TODO
41
+
42
+ ## Stack
43
+ Python 3.11+, FastAPI, SQLite, bgc CLI, React (dashboard), MCP protocol
@@ -0,0 +1,24 @@
1
+ PY = /opt/miniconda3/bin/python3.13
2
+
3
+ .PHONY: install run test validate verify lint clean
4
+
5
+ install:
6
+ $(PY) -m pip install -e .
7
+
8
+ run:
9
+ $(PY) run.py
10
+
11
+ test:
12
+ $(PY) -m pytest tests/
13
+
14
+ validate:
15
+ $(PY) -c "from bitsentry.audit_engine import AuditEngine; e=AuditEngine(); r=e.generate_audit_report(); print(r); e.export_html_report(); print('HTML report: validation/audit_report.html')"
16
+
17
+ verify:
18
+ $(PY) -c "from bitsentry.audit_engine import AuditEngine; e=AuditEngine(); r=e.generate_audit_report(); v=e.verify_integrity(r['integrity_hash']); print('Integrity verified:', v)"
19
+
20
+ lint:
21
+ $(PY) -m ruff check bitsentry/
22
+
23
+ clean:
24
+ rm -rf dist/ build/ *.egg-info __pycache__
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: bitsentry
3
+ Version: 0.1.0
4
+ Summary: Safety, audit, and intelligence layer for Bitget trading agents and traders
5
+ Project-URL: Homepage, https://github.com/Benita2001/BitSentry
6
+ Project-URL: Repository, https://github.com/Benita2001/BitSentry
7
+ Author-email: Benita Chidera <0xbeni123@gmail.com>
8
+ License: MIT
9
+ Keywords: agent,audit,bitget,crypto,mcp,risk-management,trading
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Office/Business :: Financial :: Investment
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: fastapi>=0.104.0
17
+ Requires-Dist: httpx>=0.25.0
18
+ Requires-Dist: mcp>=1.0.0
19
+ Requires-Dist: pydantic>=2.0.0
20
+ Requires-Dist: python-dotenv>=1.0.0
21
+ Requires-Dist: pyyaml>=6.0.0
22
+ Requires-Dist: requests>=2.31.0
23
+ Requires-Dist: rich>=13.0.0
24
+ Requires-Dist: uvicorn>=0.24.0
25
+ Description-Content-Type: text/markdown
26
+
27
+ # BitSentry
28
+
29
+ Safety, audit, and intelligence layer for Bitget trading agents and traders.
30
+
31
+ ## Structure
32
+
33
+ - `bitsentry/bgc_client.py` — Bitget API client
34
+ - `bitsentry/audit_engine.py` — Trade audit and logging
35
+ - `bitsentry/risk_guardian.py` — Pre-trade risk enforcement
36
+ - `bitsentry/position_monitor.py` — Real-time position tracking
37
+ - `bitsentry/strategy_evaluator.py` — Strategy scoring and auditing
38
+ - `bitsentry/api/server.py` — FastAPI REST server (11 endpoints)
39
+ - `config/risk_rules.yaml` — Configurable risk parameters
40
+ - `run.py` — Server launcher with startup banner
41
+
42
+ ## Setup
43
+
44
+ ```bash
45
+ make install # pip install -e .
46
+ make run # start server on http://127.0.0.1:8000
47
+ make validate # generate audit report + HTML
48
+ make verify # verify SHA-256 integrity hash
49
+ ```
50
+
51
+ ## API Endpoints
52
+
53
+ | Method | Path | Description |
54
+ |--------|------|-------------|
55
+ | GET | `/health` | Server health + mode |
56
+ | GET | `/positions` | Open positions with safety ratings |
57
+ | GET | `/positions/summary` | GREEN/YELLOW/RED counts |
58
+ | GET | `/positions/safe-to-trade` | Pre-trade exposure check |
59
+ | POST | `/risk/check` | 5-layer risk middleware |
60
+ | GET | `/strategy/leaderboard` | Strategies ranked by profit factor |
61
+ | GET | `/strategy/{tag}` | Strategy health verdict |
62
+ | POST | `/strategy/record` | Record a trade result |
63
+ | GET | `/audit/report` | Full audit + SHA-256 hash |
64
+ | GET | `/audit/verify` | Verify integrity hash |
65
+ | GET | `/docs` | Swagger UI |
66
+
67
+ ## Stack
68
+
69
+ Python 3.10+, FastAPI, SQLite, bgc CLI, Pydantic v2
@@ -0,0 +1,43 @@
1
+ # BitSentry
2
+
3
+ Safety, audit, and intelligence layer for Bitget trading agents and traders.
4
+
5
+ ## Structure
6
+
7
+ - `bitsentry/bgc_client.py` — Bitget API client
8
+ - `bitsentry/audit_engine.py` — Trade audit and logging
9
+ - `bitsentry/risk_guardian.py` — Pre-trade risk enforcement
10
+ - `bitsentry/position_monitor.py` — Real-time position tracking
11
+ - `bitsentry/strategy_evaluator.py` — Strategy scoring and auditing
12
+ - `bitsentry/api/server.py` — FastAPI REST server (11 endpoints)
13
+ - `config/risk_rules.yaml` — Configurable risk parameters
14
+ - `run.py` — Server launcher with startup banner
15
+
16
+ ## Setup
17
+
18
+ ```bash
19
+ make install # pip install -e .
20
+ make run # start server on http://127.0.0.1:8000
21
+ make validate # generate audit report + HTML
22
+ make verify # verify SHA-256 integrity hash
23
+ ```
24
+
25
+ ## API Endpoints
26
+
27
+ | Method | Path | Description |
28
+ |--------|------|-------------|
29
+ | GET | `/health` | Server health + mode |
30
+ | GET | `/positions` | Open positions with safety ratings |
31
+ | GET | `/positions/summary` | GREEN/YELLOW/RED counts |
32
+ | GET | `/positions/safe-to-trade` | Pre-trade exposure check |
33
+ | POST | `/risk/check` | 5-layer risk middleware |
34
+ | GET | `/strategy/leaderboard` | Strategies ranked by profit factor |
35
+ | GET | `/strategy/{tag}` | Strategy health verdict |
36
+ | POST | `/strategy/record` | Record a trade result |
37
+ | GET | `/audit/report` | Full audit + SHA-256 hash |
38
+ | GET | `/audit/verify` | Verify integrity hash |
39
+ | GET | `/docs` | Swagger UI |
40
+
41
+ ## Stack
42
+
43
+ Python 3.10+, FastAPI, SQLite, bgc CLI, Pydantic v2
@@ -0,0 +1,18 @@
1
+ # bitsentry — Safety, audit, and intelligence layer for Bitget trading agents
2
+ __version__ = "0.1.0"
3
+
4
+ from bitsentry.bgc_client import BGCClient
5
+ from bitsentry.audit_engine import AuditEngine
6
+ from bitsentry.risk_guardian import RiskGuardian, RiskCheckResult
7
+ from bitsentry.position_monitor import PositionMonitor, PositionSnapshot
8
+ from bitsentry.strategy_evaluator import StrategyEvaluator, StrategyHealth
9
+ from bitsentry.reporter import ReportGenerator
10
+ from bitsentry.scheduler import Scheduler
11
+
12
+ __all__ = [
13
+ "BGCClient", "AuditEngine",
14
+ "RiskGuardian", "RiskCheckResult",
15
+ "PositionMonitor", "PositionSnapshot",
16
+ "StrategyEvaluator", "StrategyHealth",
17
+ "ReportGenerator", "Scheduler",
18
+ ]
File without changes
@@ -0,0 +1,273 @@
1
+ from __future__ import annotations
2
+
3
+ import dataclasses
4
+ import os
5
+ from contextlib import asynccontextmanager
6
+ from typing import Any
7
+
8
+ from fastapi import FastAPI, HTTPException, Query
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+ from fastapi.responses import FileResponse
11
+ from fastapi.staticfiles import StaticFiles
12
+ from pydantic import BaseModel
13
+
14
+ from bitsentry.audit_engine import AuditEngine
15
+ from bitsentry.bgc_client import BGCClient, BGCError
16
+ from bitsentry.position_monitor import PositionMonitor
17
+ from bitsentry.reporter import ReportGenerator
18
+ from bitsentry.risk_guardian import RiskGuardian
19
+ from bitsentry.strategy_evaluator import StrategyEvaluator
20
+
21
+ # ── Global component references ───────────────────────────────────────────────
22
+ # Populated during lifespan startup; all routes read from here.
23
+
24
+ _state: dict[str, Any] = {}
25
+
26
+
27
+ def _asdict(obj: Any) -> Any:
28
+ """Recursively convert dataclasses to dicts for JSON serialization."""
29
+ if dataclasses.is_dataclass(obj) and not isinstance(obj, type):
30
+ return {k: _asdict(v) for k, v in dataclasses.asdict(obj).items()}
31
+ if isinstance(obj, list):
32
+ return [_asdict(i) for i in obj]
33
+ return obj
34
+
35
+
36
+ # ── Startup / shutdown ────────────────────────────────────────────────────────
37
+
38
+ @asynccontextmanager
39
+ async def lifespan(application: FastAPI):
40
+ demo = os.environ.get("BITGET_DEMO", "true").lower() != "false"
41
+ try:
42
+ client = BGCClient(demo=demo)
43
+ except BGCError as exc:
44
+ print(f"[bitsentry] WARNING: BGCClient failed to initialize: {exc}")
45
+ client = None
46
+
47
+ audit = AuditEngine()
48
+ guardian = RiskGuardian(audit_engine=audit)
49
+ monitor = PositionMonitor(bgc_client=client, audit_engine=audit) if client else None
50
+ evaluator = StrategyEvaluator(audit_engine=audit)
51
+
52
+ reporter = ReportGenerator(
53
+ audit_engine=audit,
54
+ strategy_evaluator=evaluator,
55
+ position_monitor=monitor,
56
+ )
57
+
58
+ _state.update({
59
+ "client": client,
60
+ "audit": audit,
61
+ "guardian": guardian,
62
+ "monitor": monitor,
63
+ "evaluator": evaluator,
64
+ "reporter": reporter,
65
+ "demo": demo,
66
+ })
67
+
68
+ print("[bitsentry] API server ready.")
69
+ yield
70
+ _state.clear()
71
+
72
+
73
+ # ── App ───────────────────────────────────────────────────────────────────────
74
+
75
+ app = FastAPI(
76
+ title="BitSentry API",
77
+ description="Safety, audit, and intelligence layer for Bitget trading agents",
78
+ version="0.1.0",
79
+ lifespan=lifespan,
80
+ )
81
+
82
+ app.add_middleware(
83
+ CORSMiddleware,
84
+ allow_origins=["*"],
85
+ allow_methods=["*"],
86
+ allow_headers=["*"],
87
+ )
88
+
89
+ # Serve dashboard static files at /dashboard/*
90
+ _dashboard_path = os.path.join(os.path.dirname(__file__), "../../dashboard")
91
+ if os.path.exists(_dashboard_path):
92
+ app.mount("/dashboard", StaticFiles(directory=_dashboard_path, html=True), name="dashboard")
93
+
94
+
95
+ # ── Request / response models ─────────────────────────────────────────────────
96
+
97
+ class RiskCheckRequest(BaseModel):
98
+ symbol: str
99
+ side: str
100
+ size_usdt: float
101
+ leverage: float
102
+ account_balance_usdt: float
103
+ daily_pnl_usdt: float
104
+ consecutive_losses: int
105
+
106
+
107
+ class RecordTradeRequest(BaseModel):
108
+ strategy_tag: str
109
+ symbol: str
110
+ side: str
111
+ entry_price: float
112
+ exit_price: float
113
+ size_usdt: float
114
+ market_condition: str = ""
115
+
116
+
117
+ # ── Routes ────────────────────────────────────────────────────────────────────
118
+
119
+ @app.get("/ui")
120
+ async def serve_dashboard():
121
+ dashboard_file = os.path.join(os.path.dirname(__file__), "../../dashboard/index.html")
122
+ return FileResponse(dashboard_file)
123
+
124
+
125
+ @app.get("/")
126
+ def root():
127
+ return {
128
+ "name": "BitSentry",
129
+ "version": "0.1.0",
130
+ "docs": "/docs",
131
+ "status": "running",
132
+ }
133
+
134
+
135
+ @app.get("/health")
136
+ def health():
137
+ return {
138
+ "status": "ok",
139
+ "version": "0.1.0",
140
+ "demo_mode": _state.get("demo", True),
141
+ }
142
+
143
+
144
+ # ── Positions ─────────────────────────────────────────────────────────────────
145
+
146
+ @app.get("/positions")
147
+ def get_positions():
148
+ monitor: PositionMonitor | None = _state.get("monitor")
149
+ if not monitor:
150
+ raise HTTPException(503, "PositionMonitor unavailable — BGCClient failed to initialize")
151
+ return _asdict(monitor.get_positions())
152
+
153
+
154
+ @app.get("/positions/summary")
155
+ def get_positions_summary():
156
+ monitor: PositionMonitor | None = _state.get("monitor")
157
+ if not monitor:
158
+ raise HTTPException(503, "PositionMonitor unavailable — BGCClient failed to initialize")
159
+ return monitor.get_account_summary()
160
+
161
+
162
+ @app.get("/positions/safe-to-trade")
163
+ def safe_to_trade(
164
+ symbol: str = Query(..., description="Trading symbol, e.g. BTCUSDT"),
165
+ direction: str = Query(..., description="long or short"),
166
+ ):
167
+ monitor: PositionMonitor | None = _state.get("monitor")
168
+ if not monitor:
169
+ raise HTTPException(503, "PositionMonitor unavailable — BGCClient failed to initialize")
170
+ return monitor.get_safe_to_trade(symbol=symbol, direction=direction)
171
+
172
+
173
+ # ── Risk ──────────────────────────────────────────────────────────────────────
174
+
175
+ @app.post("/risk/check")
176
+ def risk_check(body: RiskCheckRequest):
177
+ guardian: RiskGuardian = _state["guardian"]
178
+ result = guardian.check(
179
+ symbol=body.symbol,
180
+ side=body.side,
181
+ size_usdt=body.size_usdt,
182
+ leverage=body.leverage,
183
+ account_balance_usdt=body.account_balance_usdt,
184
+ daily_pnl_usdt=body.daily_pnl_usdt,
185
+ consecutive_losses=body.consecutive_losses,
186
+ )
187
+ return _asdict(result)
188
+
189
+
190
+ # ── Strategy — leaderboard must be declared before /{strategy_tag} ────────────
191
+
192
+ @app.get("/strategy/leaderboard")
193
+ def strategy_leaderboard():
194
+ evaluator: StrategyEvaluator = _state["evaluator"]
195
+ return evaluator.get_leaderboard()
196
+
197
+
198
+ @app.get("/strategy/{strategy_tag}")
199
+ def strategy_health(strategy_tag: str):
200
+ evaluator: StrategyEvaluator = _state["evaluator"]
201
+ health = evaluator.evaluate(strategy_tag)
202
+ return _asdict(health)
203
+
204
+
205
+ @app.post("/strategy/record")
206
+ def record_trade(body: RecordTradeRequest):
207
+ evaluator: StrategyEvaluator = _state["evaluator"]
208
+ trade_id = evaluator.record_trade_result(
209
+ strategy_tag=body.strategy_tag,
210
+ symbol=body.symbol,
211
+ side=body.side,
212
+ entry_price=body.entry_price,
213
+ exit_price=body.exit_price,
214
+ size_usdt=body.size_usdt,
215
+ market_condition=body.market_condition,
216
+ )
217
+ return {"recorded": True, "trade_id": trade_id}
218
+
219
+
220
+ # ── Audit ─────────────────────────────────────────────────────────────────────
221
+
222
+ @app.get("/audit/report")
223
+ def audit_report():
224
+ audit: AuditEngine = _state["audit"]
225
+ return audit.generate_audit_report()
226
+
227
+
228
+ @app.get("/audit/verify")
229
+ def audit_verify():
230
+ audit: AuditEngine = _state["audit"]
231
+ report = audit.generate_audit_report()
232
+ integrity_hash = report["integrity_hash"]
233
+ verified = audit.verify_integrity(integrity_hash)
234
+ return {"verified": verified, "hash": integrity_hash}
235
+
236
+
237
+ # ── Reports ───────────────────────────────────────────────────────────────────
238
+
239
+ class SendReportRequest(BaseModel):
240
+ type: str # "daily" | "weekly" | "monthly"
241
+
242
+
243
+ @app.get("/report/daily")
244
+ def report_daily():
245
+ reporter: ReportGenerator = _state["reporter"]
246
+ return {"report": reporter.generate_daily_report()}
247
+
248
+
249
+ @app.get("/report/weekly")
250
+ def report_weekly():
251
+ reporter: ReportGenerator = _state["reporter"]
252
+ return {"report": reporter.generate_weekly_report()}
253
+
254
+
255
+ @app.get("/report/monthly")
256
+ def report_monthly():
257
+ reporter: ReportGenerator = _state["reporter"]
258
+ return {"report": reporter.generate_monthly_report()}
259
+
260
+
261
+ @app.post("/report/send")
262
+ def report_send(body: SendReportRequest):
263
+ reporter: ReportGenerator = _state["reporter"]
264
+ t = body.type.lower()
265
+ if t == "daily":
266
+ sent = reporter.send_daily_report()
267
+ elif t == "weekly":
268
+ sent = reporter.send_weekly_report()
269
+ elif t == "monthly":
270
+ sent = reporter.send_monthly_report()
271
+ else:
272
+ raise HTTPException(400, f"Unknown report type '{body.type}'. Use daily, weekly, or monthly.")
273
+ return {"sent": sent, "type": t}