Skip to content

Commit 1067496

Browse files
committed
security: harden trading pipeline and fail-closed risk controls
1 parent 5399bb7 commit 1067496

45 files changed

Lines changed: 2994 additions & 1628 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,16 @@ USE_MOCK=0
5555
# --- Solana Execution [OPTIONAL] ---
5656
# Only needed if using the Solana relay/execution features
5757
# Base58 encoded private key
58-
SOL_PRIVATE_KEY=IMIT=0.02
58+
SOL_PRIVATE_KEY=
59+
60+
# --- Trading safety defaults ---
61+
# Use fractions for risk limits: 0.05 = 5%.
62+
MAX_DRAWDOWN_PCT=0.05
63+
MAX_ORDER_NOTIONAL=25000
64+
ALLOW_MARKET_ORDERS=false
65+
ALLOW_EXECUTOR_FALLBACK_TO_MOCK=false
66+
ALPACA_LIVE_TRADING=false
67+
EVENT_BUS_ALLOW_CONTROL=false
5968

6069
# ============================================================
6170
# POLYMARKET CONFIGURATION

Cargo.lock

Lines changed: 51 additions & 83 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/common/src/config.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use serde::{Deserialize, Serialize};
2+
use std::fmt;
23
use tracing::warn;
34

4-
#[derive(Debug, Serialize, Deserialize, Clone)]
5+
#[derive(Serialize, Deserialize, Clone)]
56
pub struct AppConfig {
67
// Required keys
78
pub finnhub_api_key: String,
@@ -25,6 +26,37 @@ pub struct AppConfig {
2526
pub rust_log: String,
2627
}
2728

29+
impl fmt::Debug for AppConfig {
30+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31+
f.debug_struct("AppConfig")
32+
.field("finnhub_api_key", &redact(&self.finnhub_api_key))
33+
.field("alpaca_api_key", &redact(&self.alpaca_api_key))
34+
.field("alpaca_secret_key", &"<redacted>")
35+
.field(
36+
"anthropic_api_key",
37+
&self.anthropic_api_key.as_ref().map(|k| redact(k)),
38+
)
39+
.field(
40+
"sol_private_key",
41+
&self.sol_private_key.as_ref().map(|_| "<redacted>"),
42+
)
43+
.field("use_mock", &self.use_mock)
44+
.field("alpaca_base_url", &self.alpaca_base_url)
45+
.field("rust_log", &self.rust_log)
46+
.finish()
47+
}
48+
}
49+
50+
fn redact(value: &str) -> String {
51+
let trimmed = value.trim();
52+
if trimmed.len() <= 4 {
53+
"<redacted>".to_string()
54+
} else {
55+
let prefix: String = trimmed.chars().take(4).collect();
56+
format!("{}...<redacted>", prefix)
57+
}
58+
}
59+
2860
fn default_use_mock() -> String {
2961
"0".to_string()
3062
}
@@ -57,6 +89,9 @@ impl AppConfig {
5789
if self.alpaca_api_key.trim().is_empty() {
5890
return Err("ALPACA_API_KEY cannot be empty when USE_MOCK=0".into());
5991
}
92+
if self.alpaca_secret_key.trim().is_empty() {
93+
return Err("ALPACA_SECRET_KEY cannot be empty when USE_MOCK=0".into());
94+
}
6095
}
6196

6297
if !self.alpaca_base_url.starts_with("http") {

0 commit comments

Comments
 (0)