Вы просыпаетесь утром и видите, что ваш товар на Ozon улетел на 100 страницу выдачи — цену сбили конкуренты за ночь. Handmade демпинг ведёт к нулевой марже и нераспроданному стоку. Нужна автоматизация: бот, который мониторит цены конкурентов, применяет гибкие стратегии и публикует коррекции через API маркетплейсов. Ниже — как мы это делаем.
Когда ручное управление не спасает: типичные проблемы продавца
Конкуренты меняют цены несколько раз в день — уследить самому невозможно: либо упустишь профит, либо продаёшь в минус. Многие стратегии в Excel не работают, потому что время отклика измеряется минутами, а не часами. Нет защиты от ценовых войн — два селлера гоняют цену до нуля, а третий выбрасывает товар по себестоимости. Сложно тестировать гипотезы — правило «снизить на 5%, если конкурент снизил» без репрайсинг-бота превращается в аналоговый ад.
Правильно настроенный repricing — это стабильный рост конверсии при сохранении маржинальности. Ошибка в одном коэффициенте — и вы в минусе. Поэтому бот должен уметь: соблюдать маржинальный флор, анализировать позицию в Buy Box, принудительно приостанавливаться при подозрении на ценовую войну.
Как мы проектируем репрайсинг-систему
Стек привычный: PHP 8.3 + Laravel 11 для бэкенда, PostgreSQL для хранения правил, логов и статистики. Движок репрайсинга — набор классов с чёткими ответственностями: правило (RepricingRule), движок (RepricingEngine), детектор ценовой войны (PriceWarDetector) и публикатор (OzonPricePublisher / WbPricePublisher).
Схема данных: правила репрайсинга
CREATE TABLE repricing_rules (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
marketplace VARCHAR(50) NOT NULL, -- 'ozon', 'wildberries', 'yandex_market'
scope_type VARCHAR(20) NOT NULL, -- 'global', 'category', 'product'
scope_id BIGINT,
strategy VARCHAR(30) NOT NULL, -- 'min_price', 'buy_box', 'rule_based'
min_price_mode VARCHAR(20) DEFAULT 'margin_floor',
min_price_value NUMERIC(12,2),
min_margin_pct NUMERIC(5,2) DEFAULT 10,
max_price NUMERIC(12,2),
step_pct NUMERIC(5,2) DEFAULT 1.0,
step_abs NUMERIC(10,2),
cooldown_minutes INT DEFAULT 60,
is_active BOOLEAN DEFAULT TRUE
);
CREATE TABLE repricing_log (
id BIGSERIAL PRIMARY KEY,
product_id BIGINT REFERENCES products(id),
marketplace VARCHAR(50),
old_price NUMERIC(12,2),
new_price NUMERIC(12,2),
reason TEXT,
rule_id BIGINT REFERENCES repricing_rules(id),
triggered_at TIMESTAMP DEFAULT NOW()
);
Такая структура позволяет гибко настраивать стратегии: на уровне всего магазина (global), для категории или для конкретного товара. В правилах обязательны ограничители — минимальная цена (fixed или % от себестоимости), максимальная, шаг изменения и кулдаун между публикациями. Лог — не только для отладки, но и для аналитики.
Как получать цены конкурентов: Ozon и Wildberries
Для Ozon используем эндпоинт /v1/product/info/competitor-price. Он возвращает цены конкурентов в Buy Box вместе с их рейтингом. Пример на PHP:
class OzonCompetitorPriceClient
{
public function getCompetitorPrices(string $offerId): array
{
$response = Http::withHeaders([
'Client-Id' => $this->clientId,
'Api-Key' => $this->apiKey,
])->post('https://api-seller.ozon.ru/v1/product/info/competitor-price', [
'offer_id' => $offerId,
]);
return $response->json('result', []);
}
}
Wildberries — карточка товара и парсинг card.wb.ru (в некоторых случаях API v2). Получаем массив sizes, где указаны цены для разных размеров, включая цену конкурента.
Движок репрайсинга: как принимается решение
class RepricingEngine
{
public function calculateNewPrice(
Product $product,
string $marketplace,
RepricingRule $rule,
): ?PriceDecision {
$competitorData = $this->getCompetitorData($product, $marketplace);
$costPrice = $product->cost_price ?? 0;
$currentPrice = $this->getCurrentMarketplacePrice($product, $marketplace);
$decision = match ($rule->strategy) {
'min_price' => $this->strategyMinPrice($currentPrice, $competitorData, $rule, $costPrice),
'buy_box' => $this->strategyBuyBox($currentPrice, $competitorData, $rule, $costPrice),
'rule_based' => $this->strategyRuleBased($currentPrice, $competitorData, $rule, $costPrice),
default => null,
};
if (!$decision) return null;
// Проверка cooldown
$lastChange = RepricingLog::where('product_id', $product->id)
->where('marketplace', $marketplace)
->where('triggered_at', '>=', now()->subMinutes($rule->cooldown_minutes))
->exists();
if ($lastChange) return null;
return $decision;
}
private function strategyMinPrice(
float $current, array $competitors, RepricingRule $rule, float $costPrice
): ?PriceDecision {
$competitorMin = collect($competitors)->min('price');
if (!$competitorMin) return null;
$floor = $this->calculateFloor($rule, $costPrice);
if ($competitorMin < $current) {
$newPrice = max($competitorMin, $floor);
if ($newPrice >= $current) return null;
return new PriceDecision(
newPrice: $newPrice,
reason: "Конкурент снизил цену до {$competitorMin}",
);
}
if ($competitorMin > $current && $rule->max_price && $current < $rule->max_price) {
$newPrice = min($competitorMin - 1, $rule->max_price);
return new PriceDecision(
newPrice: $newPrice,
reason: "Конкурент поднял цену до {$competitorMin}",
);
}
return null;
}
private function calculateFloor(RepricingRule $rule, float $costPrice): float
{
if ($rule->min_price_mode === 'fixed' && $rule->min_price_value) {
return $rule->min_price_value;
}
if ($rule->min_margin_pct && $costPrice > 0) {
return $costPrice * (1 + $rule->min_margin_pct / 100);
}
return 0;
}
}
Ключевое — стратегия Min Price. Минимальная цена конкурента сравнивается с текущей: если конкурент дешевле — снижаем до его цены (но не ниже вычисленного floor); если конкурент дороже и есть потолок — поднимаем. Кулдаун предотвращает частые скачки.
Как устроена защита от ценовых войн?
Ценовая война — когда два селлера бесконечно снижают цены друг за другом. Наш детектор:
class PriceWarDetector
{
public function isWarring(int $productId, string $marketplace): bool
{
$changes = RepricingLog::where('product_id', $productId)
->where('marketplace', $marketplace)
->where('triggered_at', '>=', now()->subDay())
->count();
if ($changes >= 5) {
Cache::put("repricing.paused.{$productId}.{$marketplace}", true, now()->addHours(6));
Notification::send($this->admins, new PriceWarAlert($productId, $marketplace));
return true;
}
return false;
}
}
Порог (5 изменений за 24 часа) настраивается под категорию товара. Пока бот заморожен, можно вручную разобраться или поднять цену.
Мониторинг и отчёты: SQL для аналитики
SELECT
p.name,
rl.marketplace,
COUNT(*) AS changes_count,
MIN(rl.new_price) AS min_price_today,
MAX(rl.new_price) AS max_price_today,
ROUND(AVG(rl.new_price), 2) AS avg_price_today
FROM repricing_log rl
JOIN products p ON p.id = rl.product_id
WHERE rl.triggered_at >= NOW() - INTERVAL '24 hours'
GROUP BY p.name, rl.marketplace
ORDER BY changes_count DESC;
Такой запрос выдаёт картину за день: какие товары менялись часто, в каком диапазоне цен. Можно добавить сегментацию по стратегии или категории.
Расписание задач
$schedule->job(new RunRepricingJob)->everyThirtyMinutes();
$schedule->job(new ResetRepricingCountersJob)->dailyAt('03:00');
Каждые 30 минут — запуск репрайсинга для активных правил. Ночью — сброс счётчиков. Время запуска можно менять под пиковые нагрузки конкретного маркетплейса.
Сравнение стратегий репрайсинга
| Стратегия | Описание | Когда использовать |
|---|---|---|
| Min Price | Удерживать цену на уровне минимальной среди конкурентов | Высокая конкуренция, товары с низкой дифференциацией |
| Buy Box | Ориентироваться на выигрыш в корзине (цена + рейтинг) | Товары, где Buy Box даёт 80% продаж |
| Rule-Based | Гибкие условия (например, снижать на 5% при снижении конкурента) | Сложные сценарии с сезонностью или акциями |
| Margin Floor | Защита минимальной маржи (цена не ниже себестоимости + процент) | Товары с низкой маржой |
Что входит в разработку репрайсинг-бота
Мы реализуем проект под ключ за 6–8 рабочих дней. В объём работ входит:
- Проектирование схемы данных и движка базовых стратегий (2 дня)
- Интеграция с Ozon API (цены конкурентов + публикация) (1–2 дня)
- Интеграция с Wildberries API (+1 день)
- Механизм PriceWarDetector с cooldown и алертами (1 день)
- Интерфейс управления правилами и лог изменений (1–2 дня)
- Полная документация по API (схемы запросов/ответов), инструкция по настройке правил, обучение вашей команды базовому администрированию, поддержка при запуске.
Мы гарантируем корректную интеграцию с официальными API — наш опыт включает более 50 проектов по автоматизации маркетплейсов. Стоимость фиксируется в договоре и не меняется в процессе.
Чек-лист: типичные ошибки при внедрении репрайсинга
- Нет кулдауна — цены меняются каждые 5 минут, Ozon блокирует API.
- Не указан min_price — уходите в минус по марже.
- Забыли про сторнирование — если цена ниже себестоимости, бот должен сразу остановить правку.
- Не мониторите ошибки — silent fail при публикации цены приводит к тому, что товар стоит по старой цене.
Свяжитесь с нами, чтобы обсудить ваш сценарий — мы пришлём примерное ТЗ и ответим на вопросы в течение дня.







