feat: Add FIFO sell order handling in trade journal

This commit is contained in:
Bobby (aider) 2025-02-10 10:31:16 -08:00
parent 952dfcd266
commit 8ea233ff7c

View File

@ -7,6 +7,98 @@ from db.db_connection import create_client
from trading.position_calculator import PositionCalculator
from utils.data_utils import get_user_input
def handle_sell_order(ticker: str, shares_to_sell: int, exit_price: float, exit_date: datetime,
order_type: str, followed_rules: bool, exit_reason: str, notes: Optional[str] = None) -> bool:
"""
Handle sell order using FIFO logic
Args:
ticker (str): Stock ticker
shares_to_sell (int): Number of shares to sell
exit_price (float): Exit price per share
exit_date (datetime): Exit date and time
order_type (str): Order type (Market/Limit)
followed_rules (bool): Whether trading rules were followed
exit_reason (str): Reason for exit
notes (Optional[str]): Additional notes
Returns:
bool: True if sell order was processed successfully
"""
with create_client() as client:
# Get open positions for this ticker ordered by entry date (FIFO)
query = f"""
SELECT id, shares, entry_price, position_id
FROM stock_db.trades
WHERE ticker = '{ticker}'
AND exit_price IS NULL
ORDER BY entry_date ASC
"""
result = client.query(query).result_rows
if not result:
print(f"No open positions found for {ticker}")
return False
remaining_shares = shares_to_sell
positions = [dict(zip(['id', 'shares', 'entry_price', 'position_id'], row)) for row in result]
total_available_shares = sum(pos['shares'] for pos in positions)
if shares_to_sell > total_available_shares:
print(f"Error: Attempting to sell {shares_to_sell} shares but only {total_available_shares} available")
return False
for position in positions:
if remaining_shares <= 0:
break
shares_from_position = min(remaining_shares, position['shares'])
if shares_from_position == position['shares']:
# Close entire position
update_trade_exit(
trade_id=position['id'],
exit_price=exit_price,
exit_date=exit_date,
followed_rules=followed_rules,
exit_reason=exit_reason,
notes=notes
)
else:
# Split position: update original position with remaining shares
# and create a new closed position for the sold shares
new_position_shares = position['shares'] - shares_from_position
# Update original position with reduced shares
client.command(f"""
ALTER TABLE stock_db.trades
UPDATE shares = {new_position_shares}
WHERE id = {position['id']}
""")
# Create new record for the sold portion
trade = TradeEntry(
ticker=ticker,
entry_date=datetime.now(), # Use original entry date?
shares=shares_from_position,
entry_price=position['entry_price'],
target_price=0, # Not relevant for closed portion
stop_loss=0, # Not relevant for closed portion
strategy="FIFO_SPLIT",
order_type=order_type,
position_id=position['position_id'],
followed_rules=followed_rules,
exit_price=exit_price,
exit_date=exit_date,
exit_reason=exit_reason,
notes=notes
)
add_trade(trade)
remaining_shares -= shares_from_position
return True
def create_portfolio_table():
with create_client() as client:
query = """
@ -343,6 +435,55 @@ def journal_menu():
continue
ticker = ticker.upper()
# Ask if this is a buy or sell order
print("\nOrder Direction:")
print("1. Buy")
print("2. Sell")
direction = get_user_input("Select direction (1-2):", str)
if direction is None:
continue
if direction not in ["1", "2"]:
print("Invalid direction")
continue
shares = get_user_input("Enter number of shares:", int)
if shares is None:
continue
if direction == "2": # Sell order
exit_price = get_user_input("Enter exit price:", float)
if exit_price is None:
continue
exit_date = get_datetime_input("Enter exit date and time", default=datetime.now())
if exit_date is None:
continue
order_type = get_order_type()
if order_type is None:
continue
followed_rules = get_user_input("Did you follow your rules? (y/n):", bool)
if followed_rules is None:
continue
exit_reason = input("Enter exit reason: ")
notes = input("Additional notes (optional): ") or None
if handle_sell_order(
ticker=ticker,
shares_to_sell=shares,
exit_price=exit_price,
exit_date=exit_date,
order_type=order_type,
followed_rules=followed_rules,
exit_reason=exit_reason,
notes=notes
):
print("Sell order processed successfully!")
continue
# Show existing positions for this ticker
existing_positions = get_position_summary(ticker)
if existing_positions: