90 lines
3.4 KiB
Python
90 lines
3.4 KiB
Python
"""
|
|
Executor configuration loaded from config.yaml.
|
|
|
|
Defines settings for the triangular arbitrage executor including
|
|
Unix socket path, concurrency limits, KuCoin credentials, and logging.
|
|
|
|
live_mode at the top level controls whether the executor places real
|
|
orders (live) or validates via order_test and simulates fills (paper).
|
|
"""
|
|
import asyncio
|
|
import logging
|
|
from decimal import Decimal
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
import yaml
|
|
from pydantic import BaseModel, Field
|
|
from pydantic_settings import BaseSettings
|
|
|
|
log = logging.getLogger("executor-config")
|
|
|
|
|
|
class ExecutorSettings(BaseModel):
|
|
"""Settings that control executor runtime behaviour."""
|
|
|
|
socket_path: Path = Field(
|
|
default=Path("/tmp/executor.sock"),
|
|
description="Unix domain socket path for fused_engine -> executor",
|
|
)
|
|
concurrent_slots: int = Field(default=1, ge=1, description="Max concurrent triangle executions")
|
|
enforce_same_base_isolation: bool = Field(
|
|
default=True,
|
|
description="Block concurrent triangles sharing the same base currency",
|
|
)
|
|
enforce_pair_isolation: bool = Field(
|
|
default=True,
|
|
description="Block concurrent triangles that share any trading pair symbol",
|
|
)
|
|
log_file: Path = Field(
|
|
default=Path("/tmp/executor.log"),
|
|
description="Path to log file",
|
|
)
|
|
log_level: str = Field(default="INFO", description="Logging level")
|
|
rest_port: int = Field(
|
|
default=8002,
|
|
description="HTTP REST API port (8000=fh_ob, 8001=oe_em)",
|
|
)
|
|
initial_capital: dict[str, Decimal] = Field(
|
|
default_factory=lambda: {"USDT": Decimal("10")},
|
|
description="Starting capital per currency. Caps the max_volume for each triangle's primary_quote.",
|
|
)
|
|
private_token_url: str = Field(
|
|
default="https://api.kucoin.com/api/v1/bullet-private",
|
|
description="KuCoin private bullet token endpoint",
|
|
)
|
|
fill_timeout_ms: float = Field(
|
|
default=1000,
|
|
description="Per-leg fill wait timeout in milliseconds",
|
|
)
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""Top-level settings parsed from config.yaml."""
|
|
|
|
# live_mode is the master switch: False = paper (order_test + simulated fills),
|
|
# True = real orders on KuCoin. Set at top level of config.yaml.
|
|
live_mode: bool = Field(default=False, description="false = paper; true = live orders")
|
|
executor: ExecutorSettings = Field(default_factory=ExecutorSettings)
|
|
kucoin_api_key: str = Field(default="", description="KuCoin API key")
|
|
kucoin_api_secret: str = Field(default="", description="KuCoin API secret")
|
|
kucoin_api_passphrase: str = Field(default="", description="KuCoin API passphrase")
|
|
|
|
@classmethod
|
|
async def from_yaml(cls, path: Path) -> "Settings":
|
|
"""Load settings from a YAML file, ignoring unknown keys."""
|
|
loop = asyncio.get_running_loop()
|
|
|
|
def _read() -> dict:
|
|
with open(path) as f:
|
|
return yaml.safe_load(f) or {}
|
|
|
|
data = await loop.run_in_executor(None, _read)
|
|
known = {"live_mode", "fused_engine", "executor", "kucoin_api_key", "kucoin_api_secret", "kucoin_api_passphrase"}
|
|
unknown = set(data.keys()) - known
|
|
if unknown:
|
|
log.warning("ignoring unknown config keys: %s", sorted(unknown))
|
|
return cls(**data)
|
|
|
|
model_config = {"env_prefix": "TRIArb_", "extra": "ignore"}
|