Ценовое окно — пустые ценовые сегменты
Как найти ценовые сегменты с высоким спросом и низкой конкуренцией, чтобы позиционировать товар клиента в «окно», где деньги есть, а продавцов мало.
Проблема
Большинство селлеров на Wildberries устанавливают цену одним из двух способов:
- «По ощущению» — «мне кажется, 1 500 ₽ — нормальная цена для этого товара»
- Копирование конкурентов — смотрят на первые 5 карточек в выдаче и ставят «чуть дешевле»
Результат предсказуем: все кучкуются в одном ценовом диапазоне и конкурируют за одних и тех же покупателей. Между тем в каждой категории существуют ценовые окна — сегменты, где:
- Спрос есть — покупатели готовы платить
- Конкуренция минимальна — мало продавцов работают в этом диапазоне
- Маржинальность выше — нет ценовой войны на каждый рубль
Найти эти окна вручную невозможно — нужны данные по сотням товаров с ценами, выручкой и продажами. Именно это даёт MPStats API.
Ценовое позиционирование напрямую определяет визуальную стратегию контента. Товар за 500 ₽ и товар за 5 000 ₽ нужно снимать принципиально по-разному. Найдя клиенту «окно», Fotofactor сразу предлаг ает релевантный контент-пакет: от стиля съёмки до текстов инфографики.
Пайплайн данных
Источник: POST /wb/get/category
Один эндпоинт возвращает ТОП-500 товаров категории со всеми ценовыми метриками. Этого достаточно для полного анализа ценового ландшафта.
graph LR
A["POST /wb/get/category\nТОП-500 товаров"] --> B["Ценовое распределение\nГистограмма по диапазонам"]
B --> C["Расчёт плотности\nТовары vs Выручка"]
C --> D{"Есть окна?\nOI > 1.5x"}
D -->|"Да"| E["Стратегия\nпозиционирования"]
D -->|"Нет"| F["Рекомендация:\nконкурировать контентом"]
E --> G["Контент-бриф\nпод ценовой сегмент"]
F --> G
Ключевые поля ответа
POST /wb/get/categoryAPI возвращает 8 ценовых метрик на каждый товар. Связка final_price + revenue + sales — основа для расчёта ценовых окон. Поля min/max/average/median показывают ценовую динамику товара за выбранный период.
| Поле | Тип | Описание |
|---|---|---|
final_price | number | Текущая цена товара (с учётом скидок) |
final_price_min | number | Минимальная цена за период |
final_price_max | number | Максимальная цена за период |
final_price_average | number | Средняя цена за период |
final_price_median | number | Медианная цена за период |
revenue | number | Выручка товара за период (₽) |
sales | number | Количество продаж за период |
balance | number | Остатки на складах WB |
Почему этих полей достаточно
final_price— текущая позиция товара на ценовой шкалеrevenue— сколько денег генерирует каждый ценовой сегментsales— реальный спрос (не просто показы, а покупки)balance— косвенный показатель серьёзности продавца (высокие остатки = готовность к масштабу)final_price_min/final_price_max— амплитуда ценовых манёвров (если min сильно ниже текущей — продавец использовал агрессивные скидки)
Анализ
Алгоритм поиска ценовых окон
import httpx
from collections import defaultdict
MPSTATS_TOKEN = "YOUR_TOKEN"
BASE_URL = "https://mpstats.io/api"
def fetch_category(path: str, d1: str, d2: str):
"""Загрузить ТОП-500 товаров категории."""
resp = httpx.post(
f"{BASE_URL}/wb/get/category",
headers={
"X-Mpstats-TOKEN": MPSTATS_TOKEN,
"Content-Type": "application/json",
},
json={
"startRow": 0,
"endRow": 500,
"filterModel": {},
"path": path,
"d1": d1,
"d2": d2,
},
)
return resp.json().get("data", [])
def build_price_histogram(products: list[dict], bins: list[tuple]):
"""
Построить гистограмму цен.
bins — список кортежей (min_price, max_price):
[(0, 500), (500, 1000), (1000, 2000), (2000, 5000), (5000, 99999)]
"""
segments = {}
for low, high in bins:
label = f"{low}-{high}₽" if high < 99999 else f"{low}+₽"
segments[label] = {
"range": (low, high),
"products": 0,
"total_revenue": 0,
"total_sales": 0,
"total_balance": 0,
"prices": [],
}
for p in products:
price = p.get("final_price", 0)
revenue = p.get("revenue", 0)
sales = p.get("sales", 0)
balance = p.get("balance", 0)
for label, seg in segments.items():
low, high = seg["range"]
if low <= price < high:
seg["products"] += 1
seg["total_revenue"] += revenue
seg["total_sales"] += sales
seg["total_balance"] += balance
seg["prices"].append(price)
break
return segments
def find_price_windows(segments: dict):
"""
Найти ценовые окна: сегменты с высокой выручкой,
но малым количеством товаров.
Opportunity Index = доля_выручки / доля_товаров
OI > 1.5 = ценовое окно (спрос > предложения)
OI < 0.7 = перенасыщенный сегмент
"""
total_products = sum(s["products"] for s in segments.values())
total_revenue = sum(s["total_revenue"] for s in segments.values())
if total_products == 0 or total_revenue == 0:
return []
results = []
for label, seg in segments.items():
product_share = seg["products"] / total_products
revenue_share = seg["total_revenue"] / total_revenue
oi = revenue_share / product_share if product_share > 0 else 0
avg_sales = (
seg["total_sales"] / seg["products"]
if seg["products"] > 0
else 0
)
results.append({
"segment": label,
"products": seg["products"],
"revenue": seg["total_revenue"],
"revenue_share": round(revenue_share * 100, 1),
"product_share": round(product_share * 100, 1),
"opportunity_index": round(oi, 2),
"avg_sales_per_product": round(avg_sales),
"is_window": oi > 1.5,
"is_overcrowded": oi < 0.7,
})
results.sort(key=lambda x: x["opportunity_index"], reverse=True)
return results
# --- Пример использования ---
products = fetch_category(
path="Автотовары/Ароматизаторы автомобильные",
d1="2026-01-01",
d2="2026-01-31",
)
bins = [(0, 500), (500, 1000), (1000, 2000), (2000, 5000), (5000, 99999)]
segments = build_price_histogram(products, bins)
windows = find_price_windows(segments)
for w in windows:
marker = ""
if w["is_window"]:
marker = " <-- ОКНО!"
elif w["is_overcrowded"]:
marker = " (перенасыщен)"
print(
f"{w['segment']:>12} | "
f"Товаров: {w['products']:>4} | "
f"Выручка: {w['revenue']:>12,.0f}₽ | "
f"OI: {w['opportunity_index']:.1f}x{marker}"
)
Пример выходных данных
Категория: Автотовары / Ароматизаторы автомобильные, январь 2026.
| Ценовой сегмент | Товаров | Выручка | Доля выручки | Доля товаров | OI | Статус |
|---|---|---|---|---|---|---|
| 500-1000 ₽ | 45 | 12 400 000 ₽ | 35.2% | 15.0% | 2.3x | ОКНО |
| 1000-2000 ₽ | 120 | 15 500 000 ₽ | 44.0% | 40.0% | 1.1x | Норма |
| 0-500 ₽ | 85 | 3 200 000 ₽ | 9.1% | 28.3% | 0.3x | Перенасыщен |
| 2000-5000 ₽ | 40 | 3 500 000 ₽ | 9.9% | 13.3% | 0.7x | Норма |
| 5000+ ₽ | 10 | 600 000 ₽ | 1.7% | 3.3% | 0.5x | Мало спроса |
Как читать таблицу:
- OI > 1.5x (окно) — выручка в сегменте непропорционально выше доли товаров. Здесь мало продавцов, но покупатели активно покупают. Лучшее место для входа.
- OI 0.7-1.5x (норма) — сегмент сбалансирован, конкуренция соответствует спросу.
- OI < 0.7x (перенасыщен) — слишком много продавцов на малый спрос. Ценовая война, низкая маржа.
Визуализация ценового ландшафта
pie title Распределение выручки по ценовым сегментам
"500-1000₽ (OI 2.3x) ОКНО" : 35.2
"1000-2000₽ (OI 1.1x)" : 44.0
"0-500₽ (OI 0.3x)" : 9.1
"2000-5000₽ (OI 0.7x)" : 9.9
"5000+₽ (OI 0.5x)" : 1.7
Сравните с распределением количества товаров — разница между долей выручки и долей товаров и есть «окно»:
pie title Распределение товаров по ценовым сегментам
"500-1000₽ (15% товаров)" : 15.0
"1000-2000₽ (40% товаров)" : 40.0
"0-500₽ (28% товаров)" : 28.3
"2000-5000₽ (13% товаров)" : 13.3
"5000+₽ (3% товаров)" : 3.3
Сегмент 500-1000 ₽ генерирует 35% выручки, но в нём только 15% товаров. Opportunity Index = 2.3x. Это классическое ценовое окно.
Действие Fotofactor
Шаг 1: Стратегия позиционирования
Найденное окно определяет ВСЁ: стиль фотографии, тон текстов, формат инфографики. Не бывает «универсального» контента — контент должен соответствовать ценовому сегменту покупателя. Человек, покупающий ароматизатор за 800 ₽, ожидает совершенно другую подачу, чем покупатель за 200 ₽.
На основе найденного окна (500-1000 ₽) формируем рекомендацию клиенту:
- Позиционирование товара — вывести цену в диапазон 650-850 ₽ (середина окна). Не дешевле 500 ₽ (попадёт в перенасыщенный сегмент), не дороже 1000 ₽ (конкуренция с массовым сегментом).
- Ценовая динамика — используем
final_price_min/final_price_maxконкурентов в окне, чтобы понять допустимый диапазон скидок для акций WB. - Управление остатками — по
balanceвидим, что лидеры в окне держат 500-2000 единиц. Рекомендуем клиенту аналогичные объёмы.
Шаг 2: Контент-бриф под ценовой сегмент
Каждый ценовой сегмент требует своего визуального языка:
| Ценовой сегмент | Стиль съёмки | Фокус инфографики | Тон текстов |
|---|---|---|---|
| 0-500 ₽ (бюджет) | Чистый packshot, яркий фон | Количество, выгода, «набор из 3 шт» | «Выгодно», «экономия», цифры |
| 500-1000 ₽ (окно!) | Lifestyle + детали | Качество материалов, сравнение с дорогими | «Оптимальный выбор», «качество без переплаты» |
| 1000-2000 ₽ (масс-маркет) | Профессиональная предметка | Уникальные характеристики, УТП | «Лучший в категории», экспертность |
| 2000-5000 ₽ (премиум) | Имиджевая съёмка, модель | Бренд-стори, упаковка, статус | «Премиальное качество», lifestyle |
Для найденного окна 500-1000 ₽ формируем бриф:
- Фотография: 8-10 кадров: lifestyle в интерьере автомобиля + крупные планы аромата/упаковки + сравнение размера (в руке, рядом с рулём)
- Инфографика: акцент на «качество как у дорогих, цена справедливая» — сравнительные таблицы, иконки состава
- SEO: ключевые слова ценового сегмента («ароматизатор автомобильный качественный», «ароматизатор в машину премиум», а НЕ «дешёвый ароматизатор»)
- Текст карточки: фокус на качестве, долговечности, приятных деталях. Никаких слов «дёшево» или «бюджетно»
Шаг 3: SEO-оптимизация под ценовой сегмент
Покупатели ищут по-разному в зависимости от бюджета:
| Бюджет покупателя | Типичные запросы | Стратегия SEO |
|---|---|---|
| До 500 ₽ | «недорогой X», «дешёвый X», «X набор» | Ключи «выгодно», «набор», «комплект» |
| 500-1000 ₽ | «качественный X», «X оригинал», «лучший X» | Ключи «качественный», «оригинал», «выбор» |
| 1000-2000 ₽ | «X премиум», «профессиональный X» | Ключи «премиум», «профессиональный», бренд |
| 2000+ ₽ | Название бренда, «люкс X», «подарочный X» | Брендовые запросы, «подарок», «люкс» |