Skill: anugotta/india-price-tracker (version 1.0.0)

--- name: india-price-tracker description: Track and compare product prices across popular Indian stores (Amazon India, Flipkart, Reliance Digital, Croma, Vijay Sales, Tata CLiQ, and more), compute effective prices after offers/cashback, detect arbitrage opportunities, and monitor price history with alerts. metadata: {"openclaw":{"emoji":"🇮🇳","homepage":"https://clawhub.ai/Michael-laffin/price-tracker","requires":{"bins":["python3"]}}} --- # India Price Tracker ## Overview Track prices across major Indian ecommerce stores and compare true payable cost, not just listed price. Primary stores: - Amazon India - Flipkart - Reliance Digital - Croma - Vijay Sales - Tata CLiQ Additional stores: - JioMart - Myntra - AJIO - Nykaa - Snapdeal ## Disclaimer This skill provides tracking and comparison workflows only. It does not execute purchases. Store APIs, policies, and pricing terms can change. You are responsible for complying with each platform's Terms of Service and applicable laws. Use at your own risk. The skill author/publisher/developer is not liable for direct or indirect loss, trading losses, missed opportunities, scraping/API bans, account restrictions, or other damages arising from use or misuse of this guidance. ## What is improved vs generic price trackers 1. **India-first store adapters** - Store list and naming normalized for Indian catalogs. 2. **Effective price modeling** - Calculates final payable estimate: - `effective_price = listing_price - instant_discount - coupon_discount - card_cashback + shipping` 3. **Pincode-aware availability** - Supports regional availability and shipping variance inputs. 4. **Arbitrage with fee model** - Computes net margin after platform fees and shipping. 5. **History + trend flags** - Tracks 30/60/90 day movement and volatility. 6. **Alerting modes** - Price-drop threshold - Margin threshold - Restock + price condition ## Setup On first use, read [setup.md](setup.md), then run scripts in `mock` mode first. ## Core workflows ### 1) Compare prices across stores ```bash python3 scripts/compare_prices.py \ --keyword "iPhone 15 128GB" \ --stores amazon_in,flipkart,reliance_digital,croma,vijay_sales,tata_cliq \ --report markdown ``` ### 2) Track a product with threshold alerts ```bash python3 scripts/track_product.py \ --product "Sony WH-1000XM5" \ --stores amazon_in,flipkart,croma \ --alert-below 24999 \ --alert-margin 0.18 \ --pincode 560001 ``` ### 3) Bulk monitor from CSV ```bash python3 scripts/bulk_monitor.py \ --csv examples/products.india.csv \ --margin-threshold 0.15 \ --output reports/alerts.txt ``` ### 4) Price history analysis ```bash python3 scripts/price_history.py \ --product "Samsung Galaxy S24" \ --days 60 \ --stores amazon_in,flipkart \ --trend-analysis ``` ## Strategy rules - Prefer **effective price** over headline price. - Compare same SKU/variant only (storage, color, seller condition). - Flag uncertain matches for manual review. - Never assume stock parity across stores. - Respect platform policies if implementing live adapters. ## Output format When asked to analyze products, return: 1. best store by effective price 2. next best alternative 3. arbitrage opportunity (if margin above threshold) 4. confidence notes (match quality, availability, shipping) ## Files - `scripts/compare_prices.py` - `scripts/track_product.py` - `scripts/bulk_monitor.py` - `scripts/price_history.py` - `scripts/config.py` - `examples/products.india.csv` - `README.md` ## Validation - Use [validation-checklist.md](validation-checklist.md) before switching to any live adapter mode. - Re-validate each store's terms, rate limits, and allowed integration method before production automation.

path: examples/products.india.csv
content: product,stores,alert_below,alert_margin,pincode
iPhone 15 128GB,"amazon_in,flipkart,croma,vijay_sales",68000,0.12,560001
Sony WH-1000XM5,"amazon_in,flipkart,reliance_digital,croma",24999,0.15,110001
Samsung Galaxy S24,"amazon_in,flipkart,reliance_digital,tata_cliq",59999,0.10,400001
Dyson V15 Detect,"amazon_in,croma,vijay_sales,reliance_digital",49999,0.18,600001


path: README.md
content: # India Price Tracker

Starter scripts for India-focused price comparison and monitoring.

## Included stores (normalized keys)

- `amazon_in`
- `flipkart`
- `reliance_digital`
- `croma`
- `vijay_sales`
- `tata_cliq`
- `jiomart`
- `myntra`
- `ajio`
- `nykaa`
- `snapdeal`

## Run

```bash
python3 scripts/compare_prices.py --keyword "iPhone 15" --stores amazon_in,flipkart,croma --report markdown
python3 scripts/track_product.py --product "Sony WH-1000XM5" --stores amazon_in,flipkart --alert-below 24999
python3 scripts/bulk_monitor.py --csv examples/products.india.csv --margin-threshold 0.15
python3 scripts/price_history.py --product "Samsung Galaxy S24" --days 60 --trend-analysis
```

## Notes

- Default implementation uses mock adapters for safe local testing.
- To support live integrations, implement adapter methods in `scripts/config.py` and ensure policy compliance.
- Run [setup.md](setup.md) first and use [validation-checklist.md](validation-checklist.md) before enabling live automation.



path: scripts/bulk_monitor.py
content: import argparse
import csv
from pathlib import Path
from typing import List
from config import STORES, mock_search


def parse_stores(value: str) -> List[str]:
    raw = [s.strip() for s in value.split(",") if s.strip()]
    return [s for s in raw if s in STORES]


def main():
    parser = argparse.ArgumentParser(description="Bulk monitor products from CSV.")
    parser.add_argument("--csv", required=True, dest="csv_path")
    parser.add_argument("--margin-threshold", type=float, default=0.15)
    parser.add_argument("--output", default="reports/alerts.txt")
    args = parser.parse_args()

    in_path = Path(args.csv_path)
    out_path = Path(args.output)
    out_path.parent.mkdir(parents=True, exist_ok=True)

    alerts: List[str] = []
    with in_path.open(newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            product = (row.get("product") or "").strip()
            stores = parse_stores((row.get("stores") or "").strip())
            if not product or not stores:
                continue
            rows = mock_search(product, stores)
            rows.sort(key=lambda x: x.effective_price)
            best = rows[0]
            worst = rows[-1]
            if worst.effective_price <= 0:
                continue
            spread = (worst.effective_price - best.effective_price) / worst.effective_price
            if spread >= args.margin_threshold:
                alerts.append(
                    f"{product}: {spread:.2%} spread | buy {STORES[best.store]} @ {best.effective_price:.2f} "
                    f"| compare {STORES[worst.store]} @ {worst.effective_price:.2f}"
                )

    out_path.write_text("\n".join(alerts) if alerts else "No alerts.", encoding="utf-8")
    print(f"Wrote {len(alerts)} alerts to {out_path}")


if __name__ == "__main__":
    main()



path: scripts/config.py
content: from dataclasses import dataclass
from typing import Dict, List
import random


STORES: Dict[str, str] = {
    "amazon_in": "Amazon India",
    "flipkart": "Flipkart",
    "reliance_digital": "Reliance Digital",
    "croma": "Croma",
    "vijay_sales": "Vijay Sales",
    "tata_cliq": "Tata CLiQ",
    "jiomart": "JioMart",
    "myntra": "Myntra",
    "ajio": "AJIO",
    "nykaa": "Nykaa",
    "snapdeal": "Snapdeal",
}


@dataclass
class Offer:
    instant_discount: float = 0.0
    coupon_discount: float = 0.0
    card_cashback: float = 0.0
    shipping: float = 0.0


@dataclass
class Listing:
    store: str
    title: str
    sku_hint: str
    list_price: float
    offer: Offer
    in_stock: bool
    seller_rating: float

    @property
    def effective_price(self) -> float:
        value = (
            self.list_price
            - self.offer.instant_discount
            - self.offer.coupon_discount
            - self.offer.card_cashback
            + self.offer.shipping
        )
        return round(max(0.0, value), 2)


def mock_search(keyword: str, stores: List[str]) -> List[Listing]:
    random.seed(hash(keyword) % 100000)
    results: List[Listing] = []
    for s in stores:
        base = random.randint(8000, 90000)
        instant = random.choice([0, 500, 1000, 1500, 2000])
        coupon = random.choice([0, 250, 500, 1000])
        cashback = random.choice([0, 500, 1000, 1500])
        shipping = random.choice([0, 49, 99, 149])
        in_stock = random.choice([True, True, True, False])
        rating = round(random.uniform(3.7, 4.9), 1)
        results.append(
            Listing(
                store=s,
                title=f"{keyword} - {STORES.get(s, s)}",
                sku_hint=f"{keyword[:12].upper()}-{s[:4].upper()}",
                list_price=float(base),
                offer=Offer(
                    instant_discount=float(instant),
                    coupon_discount=float(coupon),
                    card_cashback=float(cashback),
                    shipping=float(shipping),
                ),
                in_stock=in_stock,
                seller_rating=rating,
            )
        )
    return results



path: scripts/compare_prices.py
content: import argparse
from typing import List
from config import STORES, mock_search


def parse_stores(value: str) -> List[str]:
    raw = [s.strip() for s in value.split(",") if s.strip()]
    return [s for s in raw if s in STORES]


def to_markdown(keyword: str, rows) -> str:
    lines = []
    lines.append(f"# Price comparison: {keyword}")
    lines.append("")
    lines.append("| Store | List Price | Effective Price | In Stock | Rating |")
    lines.append("| --- | ---: | ---: | :---: | ---: |")
    for r in rows:
        lines.append(
            f"| {STORES[r.store]} | {r.list_price:.2f} | {r.effective_price:.2f} | "
            f"{'Yes' if r.in_stock else 'No'} | {r.seller_rating:.1f} |"
        )
    return "\n".join(lines)


def main():
    parser = argparse.ArgumentParser(description="Compare product prices across Indian stores.")
    parser.add_argument("--keyword", required=True)
    parser.add_argument("--stores", default="amazon_in,flipkart,reliance_digital,croma,vijay_sales,tata_cliq")
    parser.add_argument("--report", choices=["markdown", "text"], default="text")
    args = parser.parse_args()

    stores = parse_stores(args.stores)
    if not stores:
        raise SystemExit("No valid stores provided.")

    rows = mock_search(args.keyword, stores)
    rows.sort(key=lambda x: (x.effective_price, not x.in_stock))

    if args.report == "markdown":
        print(to_markdown(args.keyword, rows))
    else:
        print(f"Price comparison for: {args.keyword}")
        for r in rows:
            print(
                f"- {STORES[r.store]} | list={r.list_price:.2f} | effective={r.effective_price:.2f} "
                f"| stock={'yes' if r.in_stock else 'no'} | rating={r.seller_rating:.1f}"
            )


if __name__ == "__main__":
    main()



path: scripts/__init__.py
content: 


path: scripts/price_history.py
content: import argparse
import random
from typing import List
from config import STORES


def parse_stores(value: str) -> List[str]:
    raw = [s.strip() for s in value.split(",") if s.strip()]
    return [s for s in raw if s in STORES]


def mock_history(product: str, days: int, store: str) -> List[float]:
    seed = hash((product, store, days)) % 100000
    random.seed(seed)
    base = random.randint(7000, 85000)
    points = []
    for _ in range(days):
        drift = random.randint(-600, 600)
        base = max(100.0, base + drift)
        points.append(float(base))
    return points


def summarize(values: List[float]) -> str:
    low = min(values)
    high = max(values)
    avg = sum(values) / len(values)
    return f"low={low:.2f} high={high:.2f} avg={avg:.2f}"


def main():
    parser = argparse.ArgumentParser(description="Analyze price history trends.")
    parser.add_argument("--product", required=True)
    parser.add_argument("--days", type=int, default=30)
    parser.add_argument("--stores", default="amazon_in,flipkart")
    parser.add_argument("--trend-analysis", action="store_true")
    args = parser.parse_args()

    stores = parse_stores(args.stores)
    if not stores:
        raise SystemExit("No valid stores provided.")

    print(f"Price history: {args.product} ({args.days} days)")
    for s in stores:
        vals = mock_history(args.product, args.days, s)
        print(f"- {STORES[s]}: {summarize(vals)}")
        if args.trend_analysis:
            trend = "up" if vals[-1] > vals[0] else "down" if vals[-1] < vals[0] else "flat"
            print(f"  trend={trend} start={vals[0]:.2f} end={vals[-1]:.2f}")


if __name__ == "__main__":
    main()



path: scripts/track_product.py
content: import argparse
from typing import List
from config import STORES, mock_search


def parse_stores(value: str) -> List[str]:
    raw = [s.strip() for s in value.split(",") if s.strip()]
    return [s for s in raw if s in STORES]


def main():
    parser = argparse.ArgumentParser(description="Track a product and alert on price/margin.")
    parser.add_argument("--product", required=True)
    parser.add_argument("--stores", default="amazon_in,flipkart,croma")
    parser.add_argument("--alert-below", type=float, default=None)
    parser.add_argument("--alert-margin", type=float, default=0.0, help="e.g., 0.2 for 20%%")
    parser.add_argument("--pincode", default=None)
    args = parser.parse_args()

    stores = parse_stores(args.stores)
    if not stores:
        raise SystemExit("No valid stores provided.")

    rows = mock_search(args.product, stores)
    rows.sort(key=lambda x: x.effective_price)
    best = rows[0]
    worst = rows[-1]

    print(f"Tracking: {args.product}")
    if args.pincode:
        print(f"Pincode context: {args.pincode}")

    for r in rows:
        print(
            f"- {STORES[r.store]}: effective={r.effective_price:.2f}, "
            f"list={r.list_price:.2f}, stock={'yes' if r.in_stock else 'no'}"
        )

    if args.alert_below is not None and best.effective_price <= args.alert_below:
        print(f"\nALERT: Best price {best.effective_price:.2f} is below threshold {args.alert_below:.2f}.")

    if worst.effective_price > 0:
        margin = (worst.effective_price - best.effective_price) / worst.effective_price
        if margin >= args.alert_margin > 0:
            print(f"ALERT: Arbitrage spread {margin:.2%} between {STORES[best.store]} and {STORES[worst.store]}.")


if __name__ == "__main__":
    main()



path: setup.md
content: # Setup (first use)

This skill ships with mock adapters by default for safe local use.

## 1) Runtime

- Python 3 installed (`python3 --version`)

## 2) Input data

- Start with `examples/products.india.csv`
- Ensure products are normalized by exact variant (model, storage, color, condition)

## 3) Mode selection

- Start in `mock` mode only for local evaluation
- Move to `live` only after:
  - store policy review
  - rate-limit strategy
  - legal/compliance sign-off

## 4) Output handling

- Treat all outputs as **decision support**, not guaranteed truth
- Manually verify high-value opportunities before purchase decisions

## 5) Safety notes

- Do not hardcode API keys in code if adding live adapters
- Do not run aggressive scraping loops
- Respect robots, terms of use, and regional legal restrictions



path: validation-checklist.md
content: # Validation checklist

Use this before publishing live adapter support or production automation.

## Data quality

- [ ] SKU/variant matching is accurate across stores
- [ ] Outlier prices are flagged for manual review
- [ ] Stock status and delivery context are reflected in outputs

## Price logic

- [ ] Effective price formula includes discount, coupon, cashback, and shipping
- [ ] Fee assumptions for arbitrage are documented
- [ ] Currency/rounding behavior is deterministic

## Policy and compliance

- [ ] Each store's ToS allows your intended access method
- [ ] Rate limit and retry logic are safe
- [ ] No sensitive credentials in source code or chat

## Operational safety

- [ ] Alert thresholds are conservative enough to avoid noise
- [ ] Reports include confidence notes for uncertain product matches
- [ ] High-value decisions require manual verification



Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)