summaryrefslogtreecommitdiff
path: root/services/portfolio-manager/src/portfolio_manager/main.py
blob: cb7e6a85cae3babdbc9bc81a820156cde1df5f8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
"""Portfolio Manager Service entry point."""
import asyncio
import logging

from shared.broker import RedisBroker
from shared.events import Event, OrderEvent

from portfolio_manager.config import PortfolioConfig
from portfolio_manager.portfolio import PortfolioTracker

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

ORDERS_STREAM = "orders"


async def run() -> None:
    config = PortfolioConfig()
    broker = RedisBroker(config.redis_url)
    tracker = PortfolioTracker()

    last_id = "$"
    logger.info("Portfolio manager started, listening on stream=%s", ORDERS_STREAM)

    try:
        while True:
            messages = await broker.read(ORDERS_STREAM, last_id=last_id, block=1000)
            for msg in messages:
                try:
                    event = Event.from_dict(msg)
                    if isinstance(event, OrderEvent):
                        order = event.data
                        tracker.apply_order(order)
                        logger.info(
                            "Applied order symbol=%s side=%s qty=%s price=%s",
                            order.symbol,
                            order.side,
                            order.quantity,
                            order.price,
                        )
                        positions = tracker.get_all_positions()
                        logger.info("Current positions count=%d", len(positions))
                except Exception:
                    logger.exception("Failed to process message: %s", msg)
            # Update last_id to the latest processed message id if broker returns ids
            # Since broker.read returns parsed payloads (not ids), we use "$" to get new msgs
    finally:
        await broker.close()


def main() -> None:
    asyncio.run(run())


if __name__ == "__main__":
    main()