@marcos_feitoza/personal-finance-backen-trades-assets 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.0.3](https://github.com/MarcosOps/personal-finance-backend-trades-assets/compare/v1.0.2...v1.0.3) (2025-11-11)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * crypto symbol working ([9c61bf9](https://github.com/MarcosOps/personal-finance-backend-trades-assets/commit/9c61bf91b382fc7563985c1973fc6a37e9e361c1))
7
+
8
+ ## [1.0.2](https://github.com/MarcosOps/personal-finance-backend-trades-assets/compare/v1.0.1...v1.0.2) (2025-10-17)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * invest working ([d0ca897](https://github.com/MarcosOps/personal-finance-backend-trades-assets/commit/d0ca897cf8018aeecd2c10d037d94e987e241a58))
14
+
1
15
  ## [1.0.1](https://github.com/MarcosOps/personal-finance-backend-trades-assets/compare/v1.0.0...v1.0.1) (2025-09-24)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marcos_feitoza/personal-finance-backen-trades-assets",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -18,7 +18,13 @@ def create_asset(asset: schemas.AssetCreate, db: Session = Depends(get_db)):
18
18
  db_asset = db.query(models.Asset).filter(models.Asset.symbol == asset.symbol).first()
19
19
  if db_asset:
20
20
  raise HTTPException(status_code=400, detail="Asset with this symbol already exists")
21
- db_asset = models.Asset(**asset.dict())
21
+ db_asset = models.Asset(
22
+ symbol=asset.symbol,
23
+ name=asset.name,
24
+ asset_type=asset.asset_type,
25
+ industry=asset.industry,
26
+ id_crypto=asset.id_crypto
27
+ )
22
28
  db.add(db_asset)
23
29
  db.commit()
24
30
  db.refresh(db_asset)
@@ -5,6 +5,7 @@ from personal_finance_shared.database import get_db
5
5
  import logging
6
6
  import requests
7
7
  import os
8
+ import httpx
8
9
 
9
10
  logging.basicConfig(level=logging.INFO)
10
11
  logger = logging.getLogger(__name__)
@@ -14,79 +15,68 @@ router = APIRouter(
14
15
  tags=["trades"],
15
16
  )
16
17
 
17
- MARKET_DATA_SERVICE_URL = os.getenv("MARKET_DATA_SERVICE_URL", "http://personal-finance-backend-market-data:8000")
18
+ MARKET_DATA_SERVICE_URL = os.getenv("MARKET_DATA_SERVICE_URL", "http://personal-finance-backend-market-data.app.svc.cluster.local:8000")
19
+ BALANCE_SERVICE_URL = os.getenv("BALANCE_SERVICE_URL", "http://personal-finance-backend-balance-service.app.svc.cluster.local:8000")
18
20
 
19
21
  def _get_asset_details_from_service(symbol: str) -> dict:
20
22
  """Fetches asset details from the market-data service."""
21
23
  try:
22
24
  url = f"{MARKET_DATA_SERVICE_URL}/api/market-data/details/{symbol.upper()}"
23
25
  logger.info(f"Fetching details from: {url}")
24
- response = requests.post(url, timeout=10) # Using POST as per the new endpoint
25
- response.raise_for_status() # Raises HTTPError for bad responses (4xx or 5xx)
26
+ response = requests.post(url, timeout=10)
27
+ response.raise_for_status()
26
28
  return response.json()
27
29
  except requests.exceptions.RequestException as e:
28
30
  logger.error(f"Error calling market-data service for {symbol}: {e}")
29
31
  return {}
30
32
 
31
-
32
-
33
33
  @router.post("/", response_model=schemas.TradeResponse)
34
- def create_trade(trade: schemas.TradeCreate, db: Session = Depends(get_db)):
34
+ async def create_trade(trade: schemas.TradeCreate, db: Session = Depends(get_db)):
35
35
  trade_symbol = trade.symbol.upper()
36
36
  logger.info(f"Processing trade for symbol: {trade_symbol}")
37
37
 
38
- # Check if asset exists with the provided symbol or its canonical variations
39
38
  db_asset = db.query(models.Asset).filter(models.Asset.symbol == trade_symbol).first()
40
- if not db_asset and '.' not in trade_symbol:
41
- db_asset = db.query(models.Asset).filter(models.Asset.symbol == f"{trade_symbol}.TO").first()
42
39
 
43
40
  if not db_asset:
44
- logger.info(f"Asset with symbol {trade_symbol} not found. Fetching details...")
45
-
46
- asset_details = _get_asset_details_from_service(trade_symbol)
47
- canonical_symbol = asset_details.get("canonical_symbol", trade_symbol)
48
-
49
- # Double-check if an asset with the canonical symbol already exists
50
- db_asset = db.query(models.Asset).filter(models.Asset.symbol == canonical_symbol).first()
51
-
52
- if not db_asset:
53
- logger.info(f"Creating new asset with canonical symbol: {canonical_symbol}")
54
- db_asset = models.Asset(
55
- symbol=canonical_symbol,
56
- name=asset_details.get("name", canonical_symbol),
57
- asset_type=asset_details.get("asset_type", "Unknown"),
58
- industry=asset_details.get("industry", "N/A")
59
- )
60
- db.add(db_asset)
61
- db.commit()
62
- db.refresh(db_asset)
63
- logger.info(f"Created new asset with ID: {db_asset.id}")
64
- else:
65
- logger.info(f"Found existing asset with canonical symbol: {canonical_symbol}")
41
+ logger.info(f"Asset with symbol {trade_symbol} not found. Creating new asset...")
42
+ db_asset = models.Asset(
43
+ symbol=trade_symbol,
44
+ name=trade.name,
45
+ asset_type=trade.asset_type,
46
+ industry=trade.industry,
47
+ id_crypto=trade.id_crypto if hasattr(trade, 'id_crypto') else None
48
+ )
49
+ db.add(db_asset)
50
+ db.commit()
51
+ db.refresh(db_asset)
52
+ logger.info(f"Created new asset with ID: {db_asset.id}")
66
53
  else:
67
54
  logger.info(f"Found existing asset with ID: {db_asset.id}")
68
55
 
69
- # --- Balance Validation for 'buy' trades ---
70
56
  if trade.trade_type == 'buy':
71
57
  investment_account_name = trade.investment_account
72
58
  trade_cost = trade.shares * trade.price
73
-
74
- current_balance = crud.get_investment_account_balance(db=db, account_name=investment_account_name)
75
- logger.info(f"Calculated cash balance for '{investment_account_name}' is {current_balance}")
76
-
77
- if current_balance < trade_cost:
59
+ current_balance = 0
60
+ try:
61
+ async with httpx.AsyncClient() as client:
62
+ response = await client.get(f"{BALANCE_SERVICE_URL}/api/balance/{investment_account_name}")
63
+ response.raise_for_status()
64
+ data = response.json()
65
+ current_balance = data['balance']
66
+ logger.info(f"Calculated cash balance for '{investment_account_name}' is {current_balance}")
67
+ except Exception as e:
68
+ logger.error(f"Could not retrieve balance for validation: {e}")
69
+
70
+ if float(current_balance) < float(trade_cost):
78
71
  logger.warning(f"Insufficient funds in '{investment_account_name}'. Balance: {current_balance}, Required: {trade_cost}")
79
72
  raise HTTPException(
80
73
  status_code=400,
81
74
  detail=f"Insufficient cash balance in '{investment_account_name}' to complete trade."
82
75
  )
83
- # --- End Validation Logic ---
84
76
 
85
- # Create the trade record, linking it to the asset via asset_id
86
- trade_data = trade.dict(exclude={'symbol', 'name', 'asset_type', 'industry'})
77
+ trade_data = trade.dict(exclude={'symbol', 'name', 'asset_type', 'industry', 'id_crypto'})
87
78
  db_trade = models.Trade(**trade_data, asset_id=db_asset.id)
88
79
 
89
- # Log for stock purchase/sale
90
80
  if db_trade.trade_type == 'buy':
91
81
  logger.info(f"STOCK PURCHASE: Bought {db_trade.shares} shares of {trade_symbol} at ${db_trade.price:.2f} each.")
92
82
  elif db_trade.trade_type == 'sell':
@@ -111,4 +101,4 @@ def read_trades(
111
101
 
112
102
  trades = query.order_by(models.Trade.date.desc()).offset(skip).limit(limit).all()
113
103
  logger.info(f"Found {len(trades)} trades")
114
- return trades
104
+ return trades