feat: Cast Iron Scout agent prototype
- Created autonomous cast iron deal scanner - Scans eBay RSS feeds hourly for cast iron cookware - Calculates FMV based on brand, type, size - Sends Telegram alerts for deals ≥50% below FMV - Identifies Griswold, Wagner, Wapak, Birmingham, Lodge, Victor - Tracks seen items to avoid duplicate alerts - Valuation engine with size multipliers - Configurable preferences in config.json Known issue: eBay RSS unreliable - next iteration will use proper scraping
This commit is contained in:
115
agents/cast-iron-scout/valuation.py
Normal file
115
agents/cast-iron-scout/valuation.py
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Cast Iron Valuation Engine
|
||||
Determines fair market value and calculates discount percentage
|
||||
"""
|
||||
import re
|
||||
|
||||
# Base price ranges for common cast iron items (in good condition)
|
||||
BASE_PRICES = {
|
||||
'griswold': {'skillet': 150, 'griddle': 200, 'dutch_oven': 250, 'pot': 120, 'pan': 100},
|
||||
'wagner': {'skillet': 120, 'griddle': 180, 'dutch_oven': 220, 'pot': 100, 'pan': 90},
|
||||
'wapak': {'skillet': 180, 'griddle': 220, 'dutch_oven': 280, 'pot': 150, 'pan': 130},
|
||||
'birmingham': {'skillet': 160, 'griddle': 190, 'dutch_oven': 240, 'pot': 130, 'pan': 110},
|
||||
'lodge': {'skillet': 80, 'griddle': 120, 'dutch_oven': 150, 'pot': 70, 'pan': 60},
|
||||
'victor': {'skillet': 140, 'griddle': 170, 'dutch_oven': 210, 'pot': 120, 'pan': 100},
|
||||
'default': {'skillet': 100, 'griddle': 150, 'dutch_oven': 200, 'pot': 80, 'pan': 70}
|
||||
}
|
||||
|
||||
# Size multipliers
|
||||
SIZE_MULTIPLIERS = {
|
||||
'#1': 0.5, '#2': 0.6, '#3': 0.7, '#4': 0.8, '#5': 0.9,
|
||||
'#6': 1.0, '#7': 1.1, '#8': 1.2, '#9': 1.3, '#10': 1.4,
|
||||
'#11': 1.5, '#12': 1.6, '#13': 1.7, '#14': 1.8
|
||||
}
|
||||
|
||||
def identify_brand(title):
|
||||
"""Identify the brand from title"""
|
||||
title_lower = title.lower()
|
||||
|
||||
brands = ['griswold', 'wagner', 'wapak', 'birmingham', 'lodge', 'victor', 'hollowware']
|
||||
for brand in brands:
|
||||
if brand in title_lower:
|
||||
return brand
|
||||
|
||||
return 'default'
|
||||
|
||||
def identify_item_type(title):
|
||||
"""Identify the type of item from title"""
|
||||
title_lower = title.lower()
|
||||
|
||||
types = ['skillet', 'griddle', 'dutch oven', 'pot', 'pan', 'grill', 'waffle', 'mold']
|
||||
for item_type in types:
|
||||
if item_type in title_lower:
|
||||
return item_type.replace(' ', '_')
|
||||
|
||||
return 'skillet' # Default to skillet
|
||||
|
||||
def extract_size(title):
|
||||
"""Extract size number from title"""
|
||||
# Look for patterns like #8, 8 inch, size 8, etc.
|
||||
patterns = [
|
||||
r'#(\d{1,2})',
|
||||
r'size\s*(\d{1,2})',
|
||||
r'(\d{1,2})\s*inch',
|
||||
r'(\d{1,2})\s*"',
|
||||
]
|
||||
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, title, re.IGNORECASE)
|
||||
if match:
|
||||
size_num = match.group(1)
|
||||
return f"#{size_num}"
|
||||
|
||||
return '#8' # Default to #8
|
||||
|
||||
def calculate_fmv(title):
|
||||
"""
|
||||
Calculate Fair Market Value for an item
|
||||
Returns estimated FMV based on brand, type, and size
|
||||
"""
|
||||
brand = identify_brand(title)
|
||||
item_type = identify_item_type(title)
|
||||
size = extract_size(title)
|
||||
|
||||
# Get base price
|
||||
brand_prices = BASE_PRICES.get(brand, BASE_PRICES['default'])
|
||||
base_price = brand_prices.get(item_type, brand_prices['skillet'])
|
||||
|
||||
# Apply size multiplier
|
||||
multiplier = SIZE_MULTIPLIERS.get(size, 1.0)
|
||||
fmv = base_price * multiplier
|
||||
|
||||
return round(fmv, 2)
|
||||
|
||||
def is_good_deal(price, title, min_discount=50):
|
||||
"""
|
||||
Determine if an item is a good deal
|
||||
Returns (is_deal, discount_percent, fmv)
|
||||
"""
|
||||
fmv = calculate_fmv(title)
|
||||
|
||||
if price <= 0 or fmv <= 0:
|
||||
return False, 0, fmv
|
||||
|
||||
discount_percent = ((fmv - price) / fmv) * 100
|
||||
|
||||
if discount_percent >= min_discount:
|
||||
return True, discount_percent, fmv
|
||||
|
||||
return False, discount_percent, fmv
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Test the valuation engine
|
||||
test_items = [
|
||||
"Griswold #8 Skillet - Rusty",
|
||||
"Wagner Sidney O -AI - Vintage Cast Iron",
|
||||
"Wapak Funny Face Skillet #10",
|
||||
"Birmingham Stove & Range Co. #12 Skillet"
|
||||
]
|
||||
|
||||
print("Valuation Engine Test:\n")
|
||||
for title in test_items:
|
||||
fmv = calculate_fmv(title)
|
||||
print(f"{title[:50]}")
|
||||
print(f" → FMV: ${fmv}\n")
|
||||
Reference in New Issue
Block a user