feat(V2): IBKR settings + env-specific credentials

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-05-03 20:00:15 +00:00
parent 391f2c02e0
commit 3a85ff05e6
5 changed files with 309 additions and 0 deletions
+78
View File
@@ -92,6 +92,83 @@ class AlpacaSettings(_Sub):
max_leverage: int = 1
class IBKRSettings(_Sub):
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
env_prefix="IBKR_",
extra="ignore",
)
consumer_key: str | None = None
access_token: str | None = None
access_token_secret: SecretStr | None = None
signature_key_path: str | None = None
encryption_key_path: str | None = None
dh_prime: SecretStr | None = None
consumer_key_testnet: str | None = None
access_token_testnet: str | None = None
access_token_secret_testnet: SecretStr | None = None
signature_key_path_testnet: str | None = None
encryption_key_path_testnet: str | None = None
account_id_testnet: str | None = None
consumer_key_live: str | None = None
access_token_live: str | None = None
access_token_secret_live: SecretStr | None = None
signature_key_path_live: str | None = None
encryption_key_path_live: str | None = None
account_id_live: str | None = None
url_live: str = "https://api.ibkr.com/v1/api"
url_testnet: str = "https://api.ibkr.com/v1/api"
ws_url_live: str = "wss://api.ibkr.com/v1/api/ws"
ws_url_testnet: str = "wss://api.ibkr.com/v1/api/ws"
max_leverage: int = 4
ws_max_subscriptions: int = 80
ws_idle_timeout_s: int = 300
def credentials(self, env: str) -> dict:
if env == "testnet":
ck = self.consumer_key_testnet or self.consumer_key
at = self.access_token_testnet or self.access_token
ats = self.access_token_secret_testnet or self.access_token_secret
sigp = self.signature_key_path_testnet or self.signature_key_path
encp = self.encryption_key_path_testnet or self.encryption_key_path
acct = self.account_id_testnet
elif env == "mainnet":
ck = self.consumer_key_live or self.consumer_key
at = self.access_token_live or self.access_token
ats = self.access_token_secret_live or self.access_token_secret
sigp = self.signature_key_path_live or self.signature_key_path
encp = self.encryption_key_path_live or self.encryption_key_path
acct = self.account_id_live
else:
raise ValueError(f"unknown ibkr env: {env}")
missing = [
n for n, v in [
("consumer_key", ck), ("access_token", at),
("access_token_secret", ats), ("signature_key_path", sigp),
("encryption_key_path", encp), ("account_id", acct),
("dh_prime", self.dh_prime),
] if not v
]
if missing:
raise ValueError(
f"IBKR credentials not configured for env={env}: missing {missing}"
)
return {
"consumer_key": ck,
"access_token": at,
"access_token_secret": ats.get_secret_value(), # type: ignore[union-attr]
"signature_key_path": sigp,
"encryption_key_path": encp,
"account_id": acct,
"dh_prime": self.dh_prime.get_secret_value(), # type: ignore[union-attr]
}
class MacroSettings(_Sub):
model_config = SettingsConfigDict(
env_file=".env",
@@ -124,5 +201,6 @@ class Settings(_Sub):
bybit: BybitSettings = Field(default_factory=lambda: BybitSettings()) # type: ignore[call-arg]
hyperliquid: HyperliquidSettings = Field(default_factory=lambda: HyperliquidSettings()) # type: ignore[call-arg]
alpaca: AlpacaSettings = Field(default_factory=lambda: AlpacaSettings()) # type: ignore[call-arg]
ibkr: IBKRSettings = Field(default_factory=lambda: IBKRSettings()) # type: ignore[call-arg]
macro: MacroSettings = Field(default_factory=lambda: MacroSettings()) # type: ignore[call-arg]
sentiment: SentimentSettings = Field(default_factory=lambda: SentimentSettings()) # type: ignore[call-arg]