Skip to content

Commit

Permalink
Fix formatting using black
Browse files Browse the repository at this point in the history
  • Loading branch information
virattt committed Jan 14, 2025
1 parent bd8ba16 commit 7f9443b
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 190 deletions.
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ flake8 = "^6.1.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
build-backend = "poetry.core.masonry.api"

[tool.black]
line-length = 420
target-version = ['py39']
include = '\.pyi?$'
18 changes: 6 additions & 12 deletions src/agents/sentiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def sentiment_agent(state: AgentState):

for ticker in tickers:
progress.update_status("sentiment_agent", ticker, "Fetching insider trades")

# Get the insider trades
insider_trades = get_insider_trades(
ticker=ticker,
Expand All @@ -29,11 +29,9 @@ def sentiment_agent(state: AgentState):
)

progress.update_status("sentiment_agent", ticker, "Analyzing trading patterns")

# Get the signals from the insider trades
transaction_shares = pd.Series(
[t.transaction_shares for t in insider_trades]
).dropna()
transaction_shares = pd.Series([t.transaction_shares for t in insider_trades]).dropna()
bearish_condition = transaction_shares < 0
signals = np.where(bearish_condition, "bearish", "bullish").tolist()

Expand All @@ -51,19 +49,15 @@ def sentiment_agent(state: AgentState):
total_signals = len(signals)
confidence = 0 # Default confidence when there are no signals
if total_signals > 0:
confidence = (
round(max(bullish_signals, bearish_signals) / total_signals, 2) * 100
)
reasoning = (
f"Bullish signals: {bullish_signals}, Bearish signals: {bearish_signals}"
)
confidence = round(max(bullish_signals, bearish_signals) / total_signals, 2) * 100
reasoning = f"Bullish signals: {bullish_signals}, Bearish signals: {bearish_signals}"

sentiment_analysis[ticker] = {
"signal": overall_signal,
"confidence": confidence,
"reasoning": reasoning,
}

progress.update_status("sentiment_agent", ticker, "Done")

# Create the sentiment message
Expand Down
31 changes: 8 additions & 23 deletions src/agents/technicals.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def technical_analyst_agent(state: AgentState):

for ticker in tickers:
progress.update_status("technical_analyst_agent", ticker, "Analyzing price data")

# Get the historical price data
prices = get_prices(
ticker=ticker,
Expand Down Expand Up @@ -187,9 +187,7 @@ def calculate_mean_reversion_signals(prices_df):
rsi_28 = calculate_rsi(prices_df, 28)

# Mean reversion signals
price_vs_bb = (prices_df["close"].iloc[-1] - bb_lower.iloc[-1]) / (
bb_upper.iloc[-1] - bb_lower.iloc[-1]
)
price_vs_bb = (prices_df["close"].iloc[-1] - bb_lower.iloc[-1]) / (bb_upper.iloc[-1] - bb_lower.iloc[-1])

# Combine signals
if z_score.iloc[-1] < -2 and price_vs_bb < 0.2:
Expand Down Expand Up @@ -404,9 +402,7 @@ def calculate_rsi(prices_df: pd.DataFrame, period: int = 14) -> pd.Series:
return rsi


def calculate_bollinger_bands(
prices_df: pd.DataFrame, window: int = 20
) -> tuple[pd.Series, pd.Series]:
def calculate_bollinger_bands(prices_df: pd.DataFrame, window: int = 20) -> tuple[pd.Series, pd.Series]:
sma = prices_df["close"].rolling(window).mean()
std_dev = prices_df["close"].rolling(window).std()
upper_band = sma + (std_dev * 2)
Expand Down Expand Up @@ -449,20 +445,12 @@ def calculate_adx(df: pd.DataFrame, period: int = 14) -> pd.DataFrame:
df["up_move"] = df["high"] - df["high"].shift()
df["down_move"] = df["low"].shift() - df["low"]

df["plus_dm"] = np.where(
(df["up_move"] > df["down_move"]) & (df["up_move"] > 0), df["up_move"], 0
)
df["minus_dm"] = np.where(
(df["down_move"] > df["up_move"]) & (df["down_move"] > 0), df["down_move"], 0
)
df["plus_dm"] = np.where((df["up_move"] > df["down_move"]) & (df["up_move"] > 0), df["up_move"], 0)
df["minus_dm"] = np.where((df["down_move"] > df["up_move"]) & (df["down_move"] > 0), df["down_move"], 0)

# Calculate ADX
df["+di"] = 100 * (
df["plus_dm"].ewm(span=period).mean() / df["tr"].ewm(span=period).mean()
)
df["-di"] = 100 * (
df["minus_dm"].ewm(span=period).mean() / df["tr"].ewm(span=period).mean()
)
df["+di"] = 100 * (df["plus_dm"].ewm(span=period).mean() / df["tr"].ewm(span=period).mean())
df["-di"] = 100 * (df["minus_dm"].ewm(span=period).mean() / df["tr"].ewm(span=period).mean())
df["dx"] = 100 * abs(df["+di"] - df["-di"]) / (df["+di"] + df["-di"])
df["adx"] = df["dx"].ewm(span=period).mean()

Expand Down Expand Up @@ -506,10 +494,7 @@ def calculate_hurst_exponent(price_series: pd.Series, max_lag: int = 20) -> floa
"""
lags = range(2, max_lag)
# Add small epsilon to avoid log(0)
tau = [
max(1e-8, np.sqrt(np.std(np.subtract(price_series[lag:], price_series[:-lag]))))
for lag in lags
]
tau = [max(1e-8, np.sqrt(np.std(np.subtract(price_series[lag:], price_series[:-lag])))) for lag in lags]

# Return the Hurst exponent from linear fit
try:
Expand Down
36 changes: 8 additions & 28 deletions src/agents/valuation.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def valuation_agent(state: AgentState):

for ticker in tickers:
progress.update_status("valuation_agent", ticker, "Fetching financial data")

# Fetch the financial metrics
financial_metrics = get_financial_metrics(
ticker=ticker,
Expand Down Expand Up @@ -51,10 +51,7 @@ def valuation_agent(state: AgentState):

progress.update_status("valuation_agent", ticker, "Calculating owner earnings")
# Calculate working capital change
working_capital_change = (
current_financial_line_item.working_capital
- previous_financial_line_item.working_capital
)
working_capital_change = current_financial_line_item.working_capital - previous_financial_line_item.working_capital

# Owner Earnings Valuation (Buffett Method)
owner_earnings_value = calculate_owner_earnings_value(
Expand Down Expand Up @@ -96,18 +93,12 @@ def valuation_agent(state: AgentState):
# Create the reasoning
reasoning = {}
reasoning["dcf_analysis"] = {
"signal": (
"bullish" if dcf_gap > 0.15 else "bearish" if dcf_gap < -0.15 else "neutral"
),
"signal": ("bullish" if dcf_gap > 0.15 else "bearish" if dcf_gap < -0.15 else "neutral"),
"details": f"Intrinsic Value: ${dcf_value:,.2f}, Market Cap: ${market_cap:,.2f}, Gap: {dcf_gap:.1%}",
}

reasoning["owner_earnings_analysis"] = {
"signal": (
"bullish"
if owner_earnings_gap > 0.15
else "bearish" if owner_earnings_gap < -0.15 else "neutral"
),
"signal": ("bullish" if owner_earnings_gap > 0.15 else "bearish" if owner_earnings_gap < -0.15 else "neutral"),
"details": f"Owner Earnings Value: ${owner_earnings_value:,.2f}, Market Cap: ${market_cap:,.2f}, Gap: {owner_earnings_gap:.1%}",
}

Expand All @@ -117,7 +108,7 @@ def valuation_agent(state: AgentState):
"confidence": confidence,
"reasoning": reasoning,
}

progress.update_status("valuation_agent", ticker, "Done")

message = HumanMessage(
Expand Down Expand Up @@ -169,12 +160,7 @@ def calculate_owner_earnings_value(
Returns:
float: Intrinsic value with margin of safety
"""
if not all(
[
isinstance(x, (int, float))
for x in [net_income, depreciation, capex, working_capital_change]
]
):
if not all([isinstance(x, (int, float)) for x in [net_income, depreciation, capex, working_capital_change]]):
return 0

# Calculate initial owner earnings
Expand All @@ -192,9 +178,7 @@ def calculate_owner_earnings_value(

# Calculate terminal value (using perpetuity growth formula)
terminal_growth = min(growth_rate, 0.03) # Cap terminal growth at 3%
terminal_value = (future_values[-1] * (1 + terminal_growth)) / (
required_return - terminal_growth
)
terminal_value = (future_values[-1] * (1 + terminal_growth)) / (required_return - terminal_growth)
terminal_value_discounted = terminal_value / (1 + required_return) ** num_years

# Sum all values and apply margin of safety
Expand Down Expand Up @@ -225,11 +209,7 @@ def calculate_intrinsic_value(
present_values.append(present_value)

# Calculate the terminal value
terminal_value = (
cash_flows[-1]
* (1 + terminal_growth_rate)
/ (discount_rate - terminal_growth_rate)
)
terminal_value = cash_flows[-1] * (1 + terminal_growth_rate) / (discount_rate - terminal_growth_rate)
terminal_present_value = terminal_value / (1 + discount_rate) ** num_years

# Sum up the present values and terminal value
Expand Down
21 changes: 12 additions & 9 deletions src/data/cache.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,49 @@
class Cache:
"""In-memory cache for API responses."""

def __init__(self):
self._prices_cache: dict[str, list[dict[str, any]]] = {}
self._financial_metrics_cache: dict[str, list[dict[str, any]]] = {}
self._line_items_cache: dict[str, list[dict[str, any]]] = {}
self._insider_trades_cache: dict[str, list[dict[str, any]]] = {}

def get_prices(self, ticker: str) -> list[dict[str, any]] | None:
"""Get cached price data if available."""
return self._prices_cache.get(ticker)

def set_prices(self, ticker: str, data: list[dict[str, any]]):
"""Cache price data."""
self._prices_cache[ticker] = data

def get_financial_metrics(self, ticker: str) -> list[dict[str, any]]:
"""Get cached financial metrics if available."""
return self._financial_metrics_cache.get(ticker)

def set_financial_metrics(self, ticker: str, data: list[dict[str, any]]):
"""Cache financial metrics data."""
self._financial_metrics_cache[ticker] = data

def get_line_items(self, ticker: str) -> list[dict[str, any]] | None:
"""Get cached line items if available."""
return self._line_items_cache.get(ticker)

def set_line_items(self, ticker: str, data: list[dict[str, any]]):
"""Cache line items data."""
self._line_items_cache[ticker] = data

def get_insider_trades(self, ticker: str) -> list[dict[str, any]] | None:
"""Get cached insider trades if available."""
return self._insider_trades_cache.get(ticker)

def set_insider_trades(self, ticker: str, data: list[dict[str, any]]):
"""Cache insider trades data."""
self._insider_trades_cache[ticker] = data


# Global cache instance
_cache = Cache()


def get_cache() -> Cache:
"""Get the global cache instance."""
return _cache
return _cache
33 changes: 21 additions & 12 deletions src/data/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ class Price(BaseModel):
volume: int
time: str


class PriceResponse(BaseModel):
ticker: str
prices: list[Price]

prices: list[Price]


class FinancialMetrics(BaseModel):
ticker: str
calendar_date: str
Expand Down Expand Up @@ -59,22 +61,24 @@ class FinancialMetrics(BaseModel):
book_value_per_share: float | None
free_cash_flow_per_share: float | None


class FinancialMetricsResponse(BaseModel):
financial_metrics: list[FinancialMetrics]
financial_metrics: list[FinancialMetrics]


class LineItem(BaseModel):
ticker: str
report_period: str
period: str
currency: str

# Allow additional fields dynamically
model_config = {
"extra": "allow"
}
model_config = {"extra": "allow"}


class LineItemResponse(BaseModel):
search_results: list[LineItem]
search_results: list[LineItem]


class InsiderTrade(BaseModel):
ticker: str
Expand All @@ -91,37 +95,42 @@ class InsiderTrade(BaseModel):
security_title: str | None
filing_date: str


class InsiderTradeResponse(BaseModel):
insider_trades: list[InsiderTrade]
insider_trades: list[InsiderTrade]


class Position(BaseModel):
cash: float = 0.0
shares: int = 0
ticker: str


class Portfolio(BaseModel):
positions: dict[str, Position] # ticker -> Position mapping
total_cash: float = 0.0


class AnalystSignal(BaseModel):
signal: str | None = None
confidence: float | None = None
reasoning: dict | str | None = None
max_position_size: float | None = None # For risk management signals


class TickerAnalysis(BaseModel):
ticker: str
analyst_signals: dict[str, AnalystSignal] # agent_name -> signal mapping


class AgentStateData(BaseModel):
tickers: list[str]
portfolio: Portfolio
start_date: str
end_date: str
ticker_analyses: dict[str, TickerAnalysis] # ticker -> analysis mapping


class AgentStateMetadata(BaseModel):
show_reasoning: bool = False
model_config = {
"extra": "allow"
}
model_config = {"extra": "allow"}
Loading

0 comments on commit 7f9443b

Please sign in to comment.