feat: Add trading journal feature with database tracking

This commit is contained in:
Bobby (aider) 2025-02-10 09:18:16 -08:00
parent 937e52f2f7
commit 590c6fd2b7
2 changed files with 212 additions and 3 deletions

View File

@ -2,6 +2,9 @@ import warnings
from urllib3.exceptions import NotOpenSSLWarning
warnings.filterwarnings('ignore', category=NotOpenSSLWarning)
from trading.journal import (create_trades_table, TradeEntry, add_trade,
update_trade_exit, get_open_trades, get_trade_history)
import datetime
from screener.data_fetcher import validate_date_range, fetch_financial_data, get_stocks_in_time_range
from screener.t_sunnyband import run_sunny_scanner
@ -30,14 +33,118 @@ def get_scanner_parameters():
portfolio_size = get_float_input("Enter portfolio size ($) or 0 to skip position sizing: ")
return min_price, max_price, min_volume, portfolio_size
def journal_menu():
create_trades_table() # Ensure table exists
while True:
print("\nTrading Journal")
print("1. Add New Trade")
print("2. Update Existing Trade")
print("3. View Open Trades")
print("4. View Trade History")
print("5. Return to Main Menu")
choice = input("\nSelect an option (1-5): ")
if choice == "1":
ticker = input("Enter ticker symbol: ").upper()
shares = int(input("Enter number of shares: "))
entry_price = float(input("Enter entry price: "))
target_price = float(input("Enter target price: "))
stop_loss = float(input("Enter stop loss: "))
strategy = input("Enter strategy name: ")
followed_rules = input("Did you follow your rules? (y/n): ").lower() == 'y'
entry_reason = input("Enter entry reason (optional): ") or None
notes = input("Additional notes (optional): ") or None
trade = TradeEntry(
ticker=ticker,
entry_date=datetime.datetime.now(),
shares=shares,
entry_price=entry_price,
target_price=target_price,
stop_loss=stop_loss,
strategy=strategy,
followed_rules=followed_rules,
entry_reason=entry_reason,
notes=notes
)
add_trade(trade)
print(f"\nExpected Profit: ${trade.expected_profit_loss:.2f}")
print(f"Maximum Loss: ${trade.max_loss:.2f}")
print("Trade added successfully!")
elif choice == "2":
open_trades = get_open_trades()
if not open_trades:
print("No open trades to update.")
continue
print("\nOpen Trades:")
for trade in open_trades:
print(f"{trade['id']}: {trade['ticker']} - Entered at ${trade['entry_price']}")
trade_id = int(input("\nEnter trade ID to update: "))
exit_price = float(input("Enter exit price: "))
followed_rules = input("Did you follow your rules? (y/n): ").lower() == 'y'
exit_reason = input("Enter exit reason: ")
notes = input("Additional notes (optional): ") or None
update_trade_exit(
trade_id=trade_id,
exit_price=exit_price,
exit_date=datetime.datetime.now(),
followed_rules=followed_rules,
exit_reason=exit_reason,
notes=notes
)
print("Trade updated successfully!")
elif choice == "3":
open_trades = get_open_trades()
if not open_trades:
print("No open trades found.")
else:
print("\nOpen Trades:")
for trade in open_trades:
print(f"\nTicker: {trade['ticker']}")
print(f"Entry Date: {trade['entry_date']}")
print(f"Shares: {trade['shares']}")
print(f"Entry Price: ${trade['entry_price']}")
print(f"Target: ${trade['target_price']}")
print(f"Stop Loss: ${trade['stop_loss']}")
print(f"Strategy: {trade['strategy']}")
elif choice == "4":
history = get_trade_history()
if not history:
print("No trade history found.")
else:
print("\nTrade History:")
for trade in history:
profit_loss = (trade['exit_price'] - trade['entry_price']) * trade['shares']
print(f"\nTicker: {trade['ticker']}")
print(f"Entry: ${trade['entry_price']} on {trade['entry_date']}")
print(f"Exit: ${trade['exit_price']} on {trade['exit_date']}")
print(f"P/L: ${profit_loss:.2f}")
print(f"Strategy: {trade['strategy']}")
if trade['notes']:
print(f"Notes: {trade['notes']}")
print("-" * 40)
elif choice == "5":
break
def main():
print("\nStock Analysis System")
print("1. Run CANSLIM Screener")
print("2. Run Technical Scanners (SunnyBands/ATR-EMA)")
print("3. Launch Trading System")
print("4. Exit")
print("3. Launch Trading System")
print("4. Trading Journal")
print("5. Exit")
choice = input("\nSelect an option (1-4): ")
choice = input("\nSelect an option (1-5): ")
if choice == "1":
# 1⃣ Ask user for start and end date
@ -117,6 +224,9 @@ def main():
trading_main()
elif choice == "4":
journal_menu()
elif choice == "5":
print("Exiting...")
return
else:

99
src/trading/journal.py Normal file
View File

@ -0,0 +1,99 @@
from datetime import datetime
from dataclasses import dataclass
from typing import Optional
from src.db.db_connection import create_client
from src.trading.position_calculator import PositionCalculator
@dataclass
class TradeEntry:
ticker: str
entry_date: datetime
shares: int
entry_price: float
target_price: float
stop_loss: float
strategy: str
followed_rules: Optional[bool] = None
entry_reason: Optional[str] = None
exit_price: Optional[float] = None
exit_date: Optional[datetime] = None
exit_reason: Optional[str] = None
notes: Optional[str] = None
@property
def expected_profit_loss(self) -> float:
return (self.target_price - self.entry_price) * self.shares
@property
def max_loss(self) -> float:
return (self.stop_loss - self.entry_price) * self.shares
@property
def actual_profit_loss(self) -> Optional[float]:
if self.exit_price:
return (self.exit_price - self.entry_price) * self.shares
return None
def create_trades_table():
with create_client() as client:
client.execute("""
CREATE TABLE IF NOT EXISTS trades (
id SERIAL PRIMARY KEY,
ticker VARCHAR(10) NOT NULL,
entry_date TIMESTAMP NOT NULL,
shares INTEGER NOT NULL,
entry_price DECIMAL(10,2) NOT NULL,
target_price DECIMAL(10,2) NOT NULL,
stop_loss DECIMAL(10,2) NOT NULL,
strategy VARCHAR(50) NOT NULL,
followed_rules BOOLEAN,
entry_reason TEXT,
exit_price DECIMAL(10,2),
exit_date TIMESTAMP,
exit_reason TEXT,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
def add_trade(trade: TradeEntry):
with create_client() as client:
client.execute("""
INSERT INTO trades (
ticker, entry_date, shares, entry_price, target_price, stop_loss,
strategy, followed_rules, entry_reason, exit_price, exit_date,
exit_reason, notes
) VALUES (
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
)
""", (
trade.ticker, trade.entry_date, trade.shares, trade.entry_price,
trade.target_price, trade.stop_loss, trade.strategy, trade.followed_rules,
trade.entry_reason, trade.exit_price, trade.exit_date, trade.exit_reason,
trade.notes
))
def update_trade_exit(trade_id: int, exit_price: float, exit_date: datetime,
followed_rules: bool, exit_reason: str, notes: Optional[str] = None):
with create_client() as client:
client.execute("""
UPDATE trades
SET exit_price = %s, exit_date = %s, followed_rules = %s,
exit_reason = %s, notes = CASE WHEN %s IS NULL THEN notes ELSE %s END
WHERE id = %s
""", (exit_price, exit_date, followed_rules, exit_reason, notes, notes, trade_id))
def get_open_trades():
with create_client() as client:
client.execute("SELECT * FROM trades WHERE exit_price IS NULL ORDER BY entry_date DESC")
return client.fetchall()
def get_trade_history(limit: int = 50):
with create_client() as client:
client.execute("""
SELECT * FROM trades
WHERE exit_price IS NOT NULL
ORDER BY exit_date DESC
LIMIT %s
""", (limit,))
return client.fetchall()