feat(portfolio): add asset name field across API, DB, and CLI
This commit is contained in:
@@ -79,6 +79,7 @@ def portfolio_status(
|
||||
def create_portfolio(
|
||||
ctx: typer.Context,
|
||||
portfolio_id: str = typer.Argument(..., help="Portfolio identifier."),
|
||||
name: str | None = typer.Option(None, help="Asset display name."),
|
||||
symbol: str = typer.Option(..., help="Trading symbol."),
|
||||
broker: str = typer.Option("kis", help="Broker code."),
|
||||
status: str = typer.Option("enabled", help="Portfolio status."),
|
||||
@@ -96,6 +97,7 @@ def create_portfolio(
|
||||
lambda service, app_context: service.create_portfolio(
|
||||
CreatePortfolioRequest(
|
||||
portfolio_id=portfolio_id,
|
||||
name=name,
|
||||
symbol=symbol,
|
||||
broker=broker,
|
||||
status=status,
|
||||
@@ -117,6 +119,7 @@ def update_portfolio(
|
||||
portfolio_id: str = typer.Argument(..., help="Portfolio identifier."),
|
||||
broker: str | None = typer.Option(None, help="Broker code."),
|
||||
status: str | None = typer.Option(None, help="Portfolio status."),
|
||||
name: str | None = typer.Option(None, help="Asset display name."),
|
||||
symbol: str | None = typer.Option(None, help="Trading symbol."),
|
||||
strategy_name: str | None = typer.Option(None, help="Strategy name."),
|
||||
strategy_params: str | None = typer.Option(None, help="Strategy params as JSON object."),
|
||||
@@ -132,6 +135,7 @@ def update_portfolio(
|
||||
UpdatePortfolioRequest(
|
||||
broker=broker,
|
||||
status=status,
|
||||
name=name,
|
||||
symbol=symbol,
|
||||
strategy_name=strategy_name,
|
||||
strategy_params=parsed_strategy_params,
|
||||
|
||||
@@ -70,6 +70,7 @@ class Portfolio:
|
||||
portfolio_id: str
|
||||
broker: str
|
||||
status: str
|
||||
name: str
|
||||
symbol: str
|
||||
cash_balance: float = 0.0
|
||||
asset_quantity: float = 0.0
|
||||
|
||||
@@ -22,6 +22,7 @@ class PortfolioDomainRepository(Protocol):
|
||||
portfolio_id: str,
|
||||
symbol: str,
|
||||
*,
|
||||
name: str | None = None,
|
||||
broker: str = "kis",
|
||||
status: str = "enabled",
|
||||
active_strategy_id: str | None = None,
|
||||
@@ -34,6 +35,7 @@ class PortfolioDomainRepository(Protocol):
|
||||
*,
|
||||
broker: str,
|
||||
status: str,
|
||||
name: str,
|
||||
symbol: str,
|
||||
cash_balance: float,
|
||||
asset_quantity: float,
|
||||
@@ -99,6 +101,7 @@ def apply_buy(
|
||||
portfolio_id=portfolio.portfolio_id,
|
||||
broker=portfolio.broker,
|
||||
status=portfolio.status,
|
||||
name=portfolio.name,
|
||||
symbol=portfolio.symbol,
|
||||
cash_balance=float(portfolio.cash_balance) - total_cost,
|
||||
asset_quantity=float(portfolio.asset_quantity) + quantity,
|
||||
@@ -159,6 +162,7 @@ def apply_sell(
|
||||
portfolio_id=portfolio.portfolio_id,
|
||||
broker=portfolio.broker,
|
||||
status=portfolio.status,
|
||||
name=portfolio.name,
|
||||
symbol=portfolio.symbol,
|
||||
cash_balance=float(portfolio.cash_balance) + net_proceeds,
|
||||
asset_quantity=float(portfolio.asset_quantity) - quantity,
|
||||
|
||||
@@ -86,6 +86,7 @@ class PortfolioRepository(Protocol):
|
||||
portfolio_id: str,
|
||||
symbol: str,
|
||||
*,
|
||||
name: str | None = None,
|
||||
broker: str = "kis",
|
||||
status: str = "enabled",
|
||||
active_strategy_id: str | None = None,
|
||||
@@ -99,6 +100,7 @@ class PortfolioRepository(Protocol):
|
||||
*,
|
||||
broker: str,
|
||||
status: str,
|
||||
name: str,
|
||||
symbol: str,
|
||||
cash_balance: float,
|
||||
asset_quantity: float,
|
||||
|
||||
@@ -62,6 +62,7 @@ _SELECT_PORTFOLIO = """
|
||||
portfolio_id,
|
||||
broker,
|
||||
status,
|
||||
name,
|
||||
symbol,
|
||||
cash_balance,
|
||||
asset_quantity,
|
||||
@@ -214,6 +215,7 @@ class PostgresPortfolioRepository:
|
||||
portfolio_id TEXT PRIMARY KEY,
|
||||
broker TEXT NOT NULL DEFAULT 'kis',
|
||||
status TEXT NOT NULL DEFAULT 'enabled',
|
||||
name TEXT NOT NULL,
|
||||
symbol TEXT NOT NULL,
|
||||
cash_balance DOUBLE PRECISION NOT NULL,
|
||||
asset_quantity DOUBLE PRECISION NOT NULL,
|
||||
@@ -227,6 +229,9 @@ class PostgresPortfolioRepository:
|
||||
)
|
||||
"""
|
||||
)
|
||||
cur.execute("ALTER TABLE portfolios ADD COLUMN IF NOT EXISTS name TEXT")
|
||||
cur.execute("UPDATE portfolios SET name = symbol WHERE name IS NULL OR name = ''")
|
||||
cur.execute("ALTER TABLE portfolios ALTER COLUMN name SET NOT NULL")
|
||||
cur.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS portfolio_ledger (
|
||||
@@ -321,7 +326,7 @@ class PostgresPortfolioRepository:
|
||||
if row is None:
|
||||
raise ValueError("포트폴리오를 찾을 수 없습니다")
|
||||
|
||||
raw_available = row[9] if len(row) > 9 else None
|
||||
raw_available = row[10] if len(row) > 10 else None
|
||||
if raw_available is None:
|
||||
avabile_strategies = None
|
||||
elif isinstance(raw_available, list):
|
||||
@@ -333,16 +338,17 @@ class PostgresPortfolioRepository:
|
||||
portfolio_id=row[0],
|
||||
broker=row[1] or "kis",
|
||||
status=row[2] or "enabled",
|
||||
symbol=row[3],
|
||||
cash_balance=float(row[4]),
|
||||
asset_quantity=float(row[5]),
|
||||
price=float(row[6]),
|
||||
profit_buffer=float(row[7]),
|
||||
active_strategy_id=row[8] if len(row) > 8 else None,
|
||||
name=row[3],
|
||||
symbol=row[4],
|
||||
cash_balance=float(row[5]),
|
||||
asset_quantity=float(row[6]),
|
||||
price=float(row[7]),
|
||||
profit_buffer=float(row[8]),
|
||||
active_strategy_id=row[9] if len(row) > 9 else None,
|
||||
avabile_strategies=avabile_strategies,
|
||||
created_at=row[10] if len(row) > 10 else None,
|
||||
updated_at=row[11] if len(row) > 11 else None,
|
||||
last_trade_at=row[12] if len(row) > 12 else None,
|
||||
created_at=row[11] if len(row) > 11 else None,
|
||||
updated_at=row[12] if len(row) > 12 else None,
|
||||
last_trade_at=row[13] if len(row) > 13 else None,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -698,6 +704,7 @@ class PostgresPortfolioRepository:
|
||||
portfolio_id: str,
|
||||
symbol: str,
|
||||
*,
|
||||
name: str | None = None,
|
||||
broker: str = "kis",
|
||||
status: str = "enabled",
|
||||
active_strategy_id: str | None = None,
|
||||
@@ -709,17 +716,18 @@ class PostgresPortfolioRepository:
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO portfolios (
|
||||
portfolio_id, broker, status, symbol,
|
||||
portfolio_id, broker, status, name, symbol,
|
||||
cash_balance, asset_quantity, asset_price, profit_buffer,
|
||||
active_strategy_id, avabile_strategies
|
||||
)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
ON CONFLICT (portfolio_id) DO NOTHING
|
||||
""",
|
||||
(
|
||||
portfolio_id,
|
||||
broker,
|
||||
status,
|
||||
name or symbol,
|
||||
symbol,
|
||||
float(initial_cash),
|
||||
0.0,
|
||||
@@ -739,6 +747,7 @@ class PostgresPortfolioRepository:
|
||||
*,
|
||||
broker: str,
|
||||
status: str,
|
||||
name: str,
|
||||
symbol: str,
|
||||
cash_balance: float,
|
||||
asset_quantity: float,
|
||||
@@ -755,12 +764,12 @@ class PostgresPortfolioRepository:
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO portfolios (
|
||||
portfolio_id, broker, status, symbol,
|
||||
portfolio_id, broker, status, name, symbol,
|
||||
cash_balance, asset_quantity, asset_price, profit_buffer,
|
||||
active_strategy_id, avabile_strategies
|
||||
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
RETURNING
|
||||
portfolio_id, broker, status, symbol,
|
||||
portfolio_id, broker, status, name, symbol,
|
||||
cash_balance, asset_quantity, asset_price, profit_buffer,
|
||||
active_strategy_id, avabile_strategies, created_at, updated_at, last_trade_at
|
||||
""",
|
||||
@@ -768,6 +777,7 @@ class PostgresPortfolioRepository:
|
||||
portfolio_id,
|
||||
broker,
|
||||
status,
|
||||
name,
|
||||
symbol,
|
||||
float(cash_balance),
|
||||
float(asset_quantity),
|
||||
@@ -785,6 +795,7 @@ class PostgresPortfolioRepository:
|
||||
allowed = {
|
||||
"broker": "broker",
|
||||
"status": "status",
|
||||
"name": "name",
|
||||
"symbol": "symbol",
|
||||
"asset_price": "asset_price",
|
||||
"active_strategy_id": "active_strategy_id",
|
||||
@@ -812,7 +823,7 @@ class PostgresPortfolioRepository:
|
||||
SET {', '.join(assignments)}
|
||||
WHERE portfolio_id = %s
|
||||
RETURNING
|
||||
portfolio_id, broker, status, symbol,
|
||||
portfolio_id, broker, status, name, symbol,
|
||||
cash_balance, asset_quantity, asset_price, profit_buffer,
|
||||
active_strategy_id, avabile_strategies, created_at, updated_at, last_trade_at
|
||||
""",
|
||||
|
||||
@@ -35,6 +35,7 @@ class LedgerEntryResponse(BaseModel):
|
||||
class PortfolioBase(BaseModel):
|
||||
broker: str = "kis"
|
||||
status: str = "enabled"
|
||||
name: str | None = None
|
||||
symbol: str
|
||||
|
||||
|
||||
@@ -42,6 +43,7 @@ class PortfolioSummary(BaseModel):
|
||||
portfolio_id: str
|
||||
broker: str
|
||||
status: str
|
||||
name: str
|
||||
symbol: str
|
||||
cash_balance: float = 0.0
|
||||
asset_quantity: float = 0.0
|
||||
@@ -80,6 +82,7 @@ class CreatePortfolioRequest(PortfolioBase):
|
||||
class UpdatePortfolioRequest(BaseModel):
|
||||
broker: str | None = None
|
||||
status: str | None = None
|
||||
name: str | None = None
|
||||
symbol: str | None = None
|
||||
strategy_name: str | None = None
|
||||
strategy_params: dict[str, Any] | None = None
|
||||
|
||||
@@ -44,6 +44,7 @@ def _to_portfolio_summary(portfolio: Portfolio):
|
||||
portfolio_id=portfolio.portfolio_id,
|
||||
broker=portfolio.broker,
|
||||
status=portfolio.status,
|
||||
name=portfolio.name,
|
||||
symbol=portfolio.symbol,
|
||||
cash_balance=portfolio.cash_balance,
|
||||
asset_quantity=portfolio.asset_quantity,
|
||||
@@ -150,6 +151,7 @@ class PortfolioService:
|
||||
payload.portfolio_id,
|
||||
broker=payload.broker,
|
||||
status=payload.status,
|
||||
name=payload.name or payload.symbol,
|
||||
symbol=payload.symbol,
|
||||
cash_balance=payload.cash_balance,
|
||||
asset_quantity=payload.asset_quantity,
|
||||
|
||||
Reference in New Issue
Block a user