fix: address sanity-check issues + rebrand to Smaug
Co-authored-by: dodox <dodox@users.noreply.local>
This commit is contained in:
+33
-18
@@ -1,8 +1,8 @@
|
||||
import logging
|
||||
from typing import Optional
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import config
|
||||
from db.db import mark_signal_executed
|
||||
from db.db import mark_signal_executed, mark_signal_closed, get_executed_unclosed_signals
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -21,14 +21,11 @@ def _get_api():
|
||||
|
||||
|
||||
def get_portfolio_value() -> float:
|
||||
api = _get_api()
|
||||
account = api.get_account()
|
||||
return float(account.portfolio_value)
|
||||
return float(_get_api().get_account().portfolio_value)
|
||||
|
||||
|
||||
def get_open_positions_count() -> int:
|
||||
api = _get_api()
|
||||
return len(api.list_positions())
|
||||
return len(_get_api().list_positions())
|
||||
|
||||
|
||||
def execute_signal(signal: dict) -> bool:
|
||||
@@ -40,16 +37,15 @@ def execute_signal(signal: dict) -> bool:
|
||||
|
||||
try:
|
||||
api = _get_api()
|
||||
positions_count = get_open_positions_count()
|
||||
if positions_count >= config.MAX_POSITIONS:
|
||||
|
||||
if get_open_positions_count() >= config.MAX_POSITIONS:
|
||||
logger.warning(f"Max positions ({config.MAX_POSITIONS}) reached, skipping {ticker}")
|
||||
return False
|
||||
|
||||
portfolio_value = get_portfolio_value()
|
||||
allocation = portfolio_value * config.POSITION_SIZE_PCT
|
||||
|
||||
latest_trade = api.get_latest_trade(ticker)
|
||||
price = float(latest_trade.price)
|
||||
price = float(api.get_latest_trade(ticker).price)
|
||||
if price <= 0:
|
||||
logger.error(f"Invalid price for {ticker}: {price}")
|
||||
return False
|
||||
@@ -61,8 +57,8 @@ def execute_signal(signal: dict) -> bool:
|
||||
|
||||
existing_positions = {p.symbol: p for p in api.list_positions()}
|
||||
if ticker in existing_positions:
|
||||
existing_value = float(existing_positions[ticker].market_value)
|
||||
if existing_value / portfolio_value >= 0.10:
|
||||
existing_pct = float(existing_positions[ticker].market_value) / portfolio_value
|
||||
if existing_pct >= 0.10:
|
||||
logger.warning(f"Already at 10% cap for {ticker}, skipping")
|
||||
return False
|
||||
|
||||
@@ -82,13 +78,32 @@ def execute_signal(signal: dict) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def close_position_after_days(ticker: str, holding_days: Optional[int] = None):
|
||||
days = holding_days or config.HOLDING_PERIOD_DAYS
|
||||
api = _get_api()
|
||||
def close_position(ticker: str, signal_id: int) -> bool:
|
||||
try:
|
||||
api.close_position(ticker)
|
||||
logger.info(f"Closed position: {ticker} after {days} days")
|
||||
_get_api().close_position(ticker)
|
||||
mark_signal_closed(signal_id)
|
||||
logger.info(f"Closed position: {ticker} (signal {signal_id})")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to close position {ticker}: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def close_expired_positions():
|
||||
if not config.ALPACA_KEY or not config.ALPACA_SECRET:
|
||||
return
|
||||
|
||||
cutoff = datetime.utcnow() - timedelta(days=config.HOLDING_PERIOD_DAYS)
|
||||
signals = get_executed_unclosed_signals()
|
||||
|
||||
for signal in signals:
|
||||
executed_at_str = signal.get("executed_at")
|
||||
if not executed_at_str:
|
||||
continue
|
||||
try:
|
||||
executed_at = datetime.strptime(executed_at_str, "%Y-%m-%dT%H:%M:%SZ")
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if executed_at <= cutoff:
|
||||
close_position(signal["ticker"], signal["id"])
|
||||
|
||||
Reference in New Issue
Block a user