Quants API
Quants API
Quants API Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠ°ΠΌΠΈ Ρ 2 ΠΌΠ΅ΡΠΎΠ΄Π°ΠΌΠΈ Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Ρ ΠΊΠ²Π°Π½ΡΠΎΠ²ΡΠΌΠΈ Π΅Π΄ΠΈΠ½ΠΈΡΠ°ΠΌΠΈ ΡΠΎΠ²Π°ΡΠΎΠ².
ΠΠ±Π·ΠΎΡ
Quants API ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½ Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠ°ΠΌΠΈ (ΠΊΠ²Π°Π½ΡΠ°ΠΌΠΈ) - ΡΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½ΡΠΌΠΈ Π΅Π΄ΠΈΠ½ΠΈΡΠ°ΠΌΠΈ ΡΠΎΠ²Π°ΡΠΎΠ² Ρ ΡΠΈΠΊΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΌΠΈ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Π°ΠΌΠΈ ΠΈ ΡΠ΅Π½Π°ΠΌΠΈ.
ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ:
- π ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎΠ± ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠ°Ρ
- π Π€ΠΈΠ»ΡΡΡΠ°ΡΠΈΡ ΠΈ ΠΏΠΎΠΈΡΠΊ ΠΊΠ²Π°Π½ΡΠΎΠ²ΡΡ Π΅Π΄ΠΈΠ½ΠΈΡ
- π Π£ΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡΡΡ ΠΈ ΡΡΠ°ΡΡΡΠ°ΠΌΠΈ
- π° ΠΠΎΠ½ΡΡΠΎΠ»Ρ ΡΠ΅Π½ ΠΈ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²
ΠΠΎΡΡΡΠΏΠ½ΡΠ΅ ΠΌΠ΅ΡΠΎΠ΄Ρ
getInfo(request) - ΠΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎΠ± ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠ΅
const quantInfo = await quantsApi.getInfo({
quant_code: ['QUANT001', 'QUANT002']
});
getList(request?) - Π‘ΠΏΠΈΡΠΎΠΊ ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠΎΠ²
const quantList = await quantsApi.getList({
visibility: 'VISIBLE',
limit: 100
});
TypeScript ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΡ
// ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ Π·Π°ΠΏΡΠΎΡΡ
interface QuantInfoRequest {
quant_code: string[];
}
interface QuantListRequest {
visibility?: "VISIBLE" | "INVISIBLE" | "IN_SALE" | "NOT_IN_SALE" | "ARCHIVED" | "MODERATED" | "REJECTED";
status?: "ACTIVE" | "INACTIVE" | "PENDING" | "BLOCKED";
limit?: number;
cursor?: string;
}
// ΠΡΠ²Π΅ΡΡ
interface QuantInfoResponse {
items: Array<{
quant_code: string;
name: string;
description: string;
quantity: number;
price: string;
currency_code: string;
status: "ACTIVE" | "INACTIVE" | "PENDING" | "BLOCKED";
visibility: "VISIBLE" | "INVISIBLE" | "IN_SALE" | "NOT_IN_SALE" | "ARCHIVED" | "MODERATED" | "REJECTED";
created_at: string;
updated_at: string;
category_id: number;
brand: string;
seller_product_id?: string;
ozon_product_id?: number;
attributes: Array<{
attribute_id: number;
attribute_name: string;
value: string;
}>;
images: Array<{
url: string;
index: number;
is_main: boolean;
}>;
pricing: {
base_price: string;
discount_price?: string;
discount_percentage?: number;
};
stock: {
quantity: number;
reserved: number;
available: number;
};
metrics: {
views: number;
purchases: number;
conversion_rate: number;
};
}>;
total: number;
}
interface QuantListResponse {
products: Array<{
quant_code: string;
name: string;
quantity: number;
price: string;
currency_code: string;
status: "ACTIVE" | "INACTIVE" | "PENDING" | "BLOCKED";
visibility: "VISIBLE" | "INVISIBLE" | "IN_SALE" | "NOT_IN_SALE" | "ARCHIVED" | "MODERATED" | "REJECTED";
created_at: string;
updated_at: string;
category_id: number;
brand: string;
images: Array<{
url: string;
is_main: boolean;
}>;
stock: {
available: number;
reserved: number;
};
}>;
total: number;
cursor?: string;
has_next: boolean;
}
ΠΡΠΈΠΌΠ΅ΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ
ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎΠ± ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠ°Ρ
// ΠΠ΅ΡΠ°Π»ΡΠ½Π°Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΏΠΎ ΠΊΠΎΠ΄Π°ΠΌ ΠΊΠ²Π°Π½ΡΠΎΠ²
const quantInfo = await quantsApi.getInfo({
quant_code: ['QUANT001', 'QUANT002', 'QUANT003']
});
quantInfo.items.forEach(item => {
console.log(`\n=== ${item.name} (${item.quant_code}) ===`);
console.log(`ΠΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ: ${item.quantity} ΡΡ`);
console.log(`Π¦Π΅Π½Π°: ${item.price} ${item.currency_code}`);
console.log(`Π‘ΡΠ°ΡΡΡ: ${item.status}`);
console.log(`ΠΠΈΠ΄ΠΈΠΌΠΎΡΡΡ: ${item.visibility}`);
if (item.pricing.discount_price) {
console.log(`Π‘ΠΊΠΈΠ΄ΠΊΠ°: ${item.pricing.discount_percentage}%`);
console.log(`Π¦Π΅Π½Π° ΡΠΎ ΡΠΊΠΈΠ΄ΠΊΠΎΠΉ: ${item.pricing.discount_price} ${item.currency_code}`);
}
console.log(`ΠΡΡΠ°ΡΠΎΠΊ: ${item.stock.available} ΠΈΠ· ${item.stock.quantity}`);
console.log(`ΠΠΎΠ½Π²Π΅ΡΡΠΈΡ: ${item.metrics.conversion_rate}% (${item.metrics.purchases} ΠΏΠΎΠΊΡΠΏΠΎΠΊ ΠΈΠ· ${item.metrics.views} ΠΏΡΠΎΡΠΌΠΎΡΡΠΎΠ²)`);
// ΠΡΡΠΈΠ±ΡΡΡ ΡΠΎΠ²Π°ΡΠ°
if (item.attributes.length > 0) {
console.log("\nΠΡΡΠΈΠ±ΡΡΡ:");
item.attributes.forEach(attr => {
console.log(` ${attr.attribute_name}: ${attr.value}`);
});
}
});
ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΡΠΏΠΈΡΠΊΠ° ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠΎΠ²
// ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π²ΡΠ΅Ρ
Π²ΠΈΠ΄ΠΈΠΌΡΡ
ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠΎΠ²
let allQuants: any[] = [];
let cursor: string | undefined;
do {
const response = await quantsApi.getList({
visibility: 'VISIBLE',
status: 'ACTIVE',
limit: 100,
cursor
});
allQuants.push(...response.products);
cursor = response.cursor;
console.log(`ΠΠ°Π³ΡΡΠΆΠ΅Π½ΠΎ ΡΠΎΠ²Π°ΡΠΎΠ²: ${allQuants.length} ΠΈΠ· ${response.total}`);
// ΠΠ½Π°Π»ΠΈΠ· ΠΏΠΎ Ρ
ΠΎΠ΄Ρ Π·Π°Π³ΡΡΠ·ΠΊΠΈ
const lowStockItems = response.products.filter(p => p.stock.available < 10);
if (lowStockItems.length > 0) {
console.log(`β οΈ Π’ΠΎΠ²Π°ΡΠΎΠ² Ρ Π½ΠΈΠ·ΠΊΠΈΠΌ ΠΎΡΡΠ°ΡΠΊΠΎΠΌ Π² ΡΠ΅ΠΊΡΡΠ΅ΠΉ ΠΏΠ°ΡΡΠΈΠΈ: ${lowStockItems.length}`);
}
} while (cursor && response.has_next);
console.log(`\nΠΠ±ΡΠ°Ρ ΡΡΠ°ΡΠΈΡΡΠΈΠΊΠ° ΠΏΠΎ ${allQuants.length} ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠ°ΠΌ:`);
console.log(`Π‘ΡΠ΅Π΄Π½ΡΡ ΡΠ΅Π½Π°: ${calculateAveragePrice(allQuants)} ΡΡΠ±.`);
console.log(`Π’ΠΎΠ²Π°ΡΠΎΠ² Ρ ΠΎΡΡΠ°ΡΠΊΠΎΠΌ < 10: ${allQuants.filter(p => p.stock.available < 10).length}`);
function calculateAveragePrice(products: any[]): string {
const total = products.reduce((sum, p) => sum + parseFloat(p.price), 0);
return (total / products.length).toFixed(2);
}
ΠΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³ ΡΡΠ°ΡΡΡΠ° ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠΎΠ²
// ΠΠ½Π°Π»ΠΈΠ· ΡΡΠ°ΡΡΡΠΎΠ² ΠΈ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡΠΈ ΡΠΎΠ²Π°ΡΠΎΠ²
const statusAnalysis = await quantsApi.getList({
limit: 1000
});
// ΠΡΡΠΏΠΏΠΈΡΠΎΠ²ΠΊΠ° ΠΏΠΎ ΡΡΠ°ΡΡΡΠ°ΠΌ
const statusGroups = statusAnalysis.products.reduce((groups, product) => {
const key = `${product.status}_${product.visibility}`;
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(product);
return groups;
}, {} as Record<string, any[]>);
console.log("\n=== ΠΠ½Π°Π»ΠΈΠ· ΡΡΠ°ΡΡΡΠΎΠ² ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠΎΠ² ===");
Object.entries(statusGroups).forEach(([status, products]) => {
console.log(`${status}: ${products.length} ΡΠΎΠ²Π°ΡΠΎΠ²`);
// ΠΠΎΠΊΠ°Π·Π°ΡΡ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ½ΡΠ΅ ΡΠΎΠ²Π°ΡΡ
if (status.includes('INACTIVE') || status.includes('BLOCKED')) {
console.log(" ΠΡΠΎΠ±Π»Π΅ΠΌΠ½ΡΠ΅ ΡΠΎΠ²Π°ΡΡ:");
products.slice(0, 5).forEach(p => {
console.log(` - ${p.name} (${p.quant_code})`);
});
if (products.length > 5) {
console.log(` ... ΠΈ Π΅ΡΠ΅ ${products.length - 5}`);
}
}
});
Π‘Π»ΠΎΠΆΠ½ΡΠ΅ ΡΡΠ΅Π½Π°ΡΠΈΠΈ
QuantAnalyticsSystem - Π‘ΠΈΡΡΠ΅ΠΌΠ° Π°Π½Π°Π»ΠΈΡΠΈΠΊΠΈ ΡΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΠΎΠ²
class QuantAnalyticsSystem {
constructor(private api: QuantsApi) {}
async generatePerformanceReport(): Promise<QuantPerformanceReport> {
// ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ Π²ΡΠ΅Ρ
Π°ΠΊΡΠΈΠ²Π½ΡΡ
ΠΊΠ²Π°Π½ΡΠΎΠ²
const activeQuants = await this.getAllActiveQuants();
// ΠΠ΅ΡΠ°Π»ΡΠ½Π°Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ Π΄Π»Ρ Π°Π½Π°Π»ΠΈΠ·Π°
const detailedInfo = await this.getDetailedQuantInfo(activeQuants);
// ΠΠ½Π°Π»ΠΈΠ· ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ
const performanceMetrics = this.calculatePerformanceMetrics(detailedInfo);
// Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°ΡΠΈΠΈ ΠΏΠΎ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠΈ
const recommendations = this.generateOptimizationRecommendations(detailedInfo);
return {
report_date: new Date().toISOString(),
total_quants: activeQuants.length,
performance_metrics: performanceMetrics,
top_performers: this.identifyTopPerformers(detailedInfo),
underperformers: this.identifyUnderperformers(detailedInfo),
optimization_recommendations: recommendations,
inventory_alerts: this.generateInventoryAlerts(detailedInfo)
};
}
private async getAllActiveQuants(): Promise<QuantProduct[]> {
const quants: QuantProduct[] = [];
let cursor: string | undefined;
do {
const response = await this.api.getList({
status: 'ACTIVE',
visibility: 'IN_SALE',
limit: 500,
cursor
});
quants.push(...response.products);
cursor = response.cursor;
} while (cursor);
return quants;
}
private async getDetailedQuantInfo(quants: QuantProduct[]): Promise<DetailedQuantInfo[]> {
const batchSize = 50;
const detailedInfo: DetailedQuantInfo[] = [];
for (let i = 0; i < quants.length; i += batchSize) {
const batch = quants.slice(i, i + batchSize);
const quantCodes = batch.map(q => q.quant_code);
const infoResponse = await this.api.getInfo({ quant_code: quantCodes });
detailedInfo.push(...infoResponse.items.map(item => ({
...item,
efficiency_score: this.calculateEfficiencyScore(item),
profitability_score: this.calculateProfitabilityScore(item),
market_position: this.analyzeMarketPosition(item)
})));
// Rate limiting
await new Promise(resolve => setTimeout(resolve, 100));
}
return detailedInfo;
}
private calculatePerformanceMetrics(quantsInfo: DetailedQuantInfo[]): PerformanceMetrics {
const totalViews = quantsInfo.reduce((sum, q) => sum + q.metrics.views, 0);
const totalPurchases = quantsInfo.reduce((sum, q) => sum + q.metrics.purchases, 0);
const totalRevenue = quantsInfo.reduce((sum, q) => sum + (q.metrics.purchases * parseFloat(q.price)), 0);
return {
overall_conversion_rate: totalPurchases / totalViews,
average_conversion_rate: quantsInfo.reduce((sum, q) => sum + q.metrics.conversion_rate, 0) / quantsInfo.length,
total_revenue: totalRevenue,
average_revenue_per_quant: totalRevenue / quantsInfo.length,
high_performers_count: quantsInfo.filter(q => q.efficiency_score > 0.8).length,
low_performers_count: quantsInfo.filter(q => q.efficiency_score < 0.3).length,
out_of_stock_count: quantsInfo.filter(q => q.stock.available === 0).length,
low_stock_count: quantsInfo.filter(q => q.stock.available > 0 && q.stock.available < 10).length
};
}
private generateOptimizationRecommendations(quantsInfo: DetailedQuantInfo[]): OptimizationRecommendation[] {
const recommendations: OptimizationRecommendation[] = [];
// ΠΠ½Π°Π»ΠΈΠ· ΡΠ΅Π½
const overpriced = quantsInfo.filter(q => q.efficiency_score < 0.3 && q.metrics.views > 100);
if (overpriced.length > 0) {
recommendations.push({
type: "PRICE_OPTIMIZATION",
priority: "HIGH",
affected_quants: overpriced.length,
description: `${overpriced.length} ΡΠΎΠ²Π°ΡΠΎΠ² ΠΈΠΌΠ΅ΡΡ Π½ΠΈΠ·ΠΊΡΡ ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎΡΡΡ ΠΏΡΠΈ Π²ΡΡΠΎΠΊΠΎΠΌ ΡΡΠ°ΡΠΈΠΊΠ΅ - ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΡΡΡ ΡΠ½ΠΈΠΆΠ΅Π½ΠΈΠ΅ ΡΠ΅Π½`,
expected_impact: "Π£Π²Π΅Π»ΠΈΡΠ΅Π½ΠΈΠ΅ ΠΊΠΎΠ½Π²Π΅ΡΡΠΈΠΈ Π½Π° 15-25%"
});
}
// ΠΠ½Π°Π»ΠΈΠ· ΠΎΡΡΠ°ΡΠΊΠΎΠ²
const overstocked = quantsInfo.filter(q =>
q.stock.available > 50 && q.metrics.purchases / q.stock.available < 0.1
);
if (overstocked.length > 0) {
recommendations.push({
type: "INVENTORY_OPTIMIZATION",
priority: "MEDIUM",
affected_quants: overstocked.length,
description: `${overstocked.length} ΡΠΎΠ²Π°ΡΠΎΠ² ΠΈΠΌΠ΅ΡΡ ΠΈΠ·Π±ΡΡΠΎΡΠ½ΡΠ΅ ΠΎΡΡΠ°ΡΠΊΠΈ Ρ Π½ΠΈΠ·ΠΊΠΎΠΉ ΠΎΠ±ΠΎΡΠ°ΡΠΈΠ²Π°Π΅ΠΌΠΎΡΡΡΡ`,
expected_impact: "ΠΡΠ²ΠΎΠ±ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ ΡΠΊΠ»Π°Π΄ΡΠΊΠΈΡ
ΠΏΠ»ΠΎΡΠ°Π΄Π΅ΠΉ, ΡΠ½ΠΈΠΆΠ΅Π½ΠΈΠ΅ Π·Π°ΡΡΠ°Ρ Π½Π° Ρ
ΡΠ°Π½Π΅Π½ΠΈΠ΅"
});
}
// ΠΠ½Π°Π»ΠΈΠ· ΡΡΠ΅Π½Π΄ΠΎΠ²
const trendingProducts = quantsInfo.filter(q =>
q.metrics.conversion_rate > 0.05 && q.stock.available < 20
);
if (trendingProducts.length > 0) {
recommendations.push({
type: "STOCK_REPLENISHMENT",
priority: "HIGH",
affected_quants: trendingProducts.length,
description: `${trendingProducts.length} Π²ΡΡΠΎΠΊΠΎΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΡΡ
ΡΠΎΠ²Π°ΡΠΎΠ² Π½ΡΠΆΠ΄Π°ΡΡΡΡ Π² ΠΏΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ ΠΎΡΡΠ°ΡΠΊΠΎΠ²`,
expected_impact: "ΠΡΠ΅Π΄ΠΎΡΠ²ΡΠ°ΡΠ΅Π½ΠΈΠ΅ ΠΏΠΎΡΠ΅ΡΠΈ ΠΏΡΠΎΠ΄Π°ΠΆ, ΡΠ²Π΅Π»ΠΈΡΠ΅Π½ΠΈΠ΅ Π²ΡΡΡΡΠΊΠΈ Π½Π° 20-30%"
});
}
return recommendations.sort((a, b) => {
const priorityOrder = { "HIGH": 3, "MEDIUM": 2, "LOW": 1 };
return priorityOrder[b.priority] - priorityOrder[a.priority];
});
}
private generateInventoryAlerts(quantsInfo: DetailedQuantInfo[]): InventoryAlert[] {
const alerts: InventoryAlert[] = [];
quantsInfo.forEach(quant => {
// ΠΡΠΈΡΠΈΡΠ΅ΡΠΊΠΈ Π½ΠΈΠ·ΠΊΠΈΠΉ ΠΎΡΡΠ°ΡΠΎΠΊ
if (quant.stock.available === 0 && quant.metrics.conversion_rate > 0.02) {
alerts.push({
level: "CRITICAL",
quant_code: quant.quant_code,
message: "Π’ΠΎΠ²Π°Ρ Π·Π°ΠΊΠΎΠ½ΡΠΈΠ»ΡΡ ΠΏΡΠΈ Π²ΡΡΠΎΠΊΠΎΠΉ ΠΊΠΎΠ½Π²Π΅ΡΡΠΈΠΈ",
suggested_action: "Π‘ΡΠΎΡΠ½ΠΎΠ΅ ΠΏΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΠΎΡΡΠ°ΡΠΊΠΎΠ²"
});
}
// ΠΠΈΠ·ΠΊΠΈΠΉ ΠΎΡΡΠ°ΡΠΎΠΊ Π²ΡΡΠΎΠΊΠΎΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎΠ³ΠΎ ΡΠΎΠ²Π°ΡΠ°
else if (quant.stock.available < 5 && quant.efficiency_score > 0.7) {
alerts.push({
level: "WARNING",
quant_code: quant.quant_code,
message: "ΠΠΈΠ·ΠΊΠΈΠΉ ΠΎΡΡΠ°ΡΠΎΠΊ ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎΠ³ΠΎ ΡΠΎΠ²Π°ΡΠ°",
suggested_action: "ΠΠ»Π°Π½ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ ΠΎΡΡΠ°ΡΠΊΠΎΠ²"
});
}
// ΠΠ·Π±ΡΡΠΎΡΠ½ΡΠΉ ΠΎΡΡΠ°ΡΠΎΠΊ Π½Π΅ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎΠ³ΠΎ ΡΠΎΠ²Π°ΡΠ°
else if (quant.stock.available > 100 && quant.efficiency_score < 0.2) {
alerts.push({
level: "INFO",
quant_code: quant.quant_code,
message: "ΠΠ·Π±ΡΡΠΎΡΠ½ΡΠΉ ΠΎΡΡΠ°ΡΠΎΠΊ Π½ΠΈΠ·ΠΊΠΎΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎΠ³ΠΎ ΡΠΎΠ²Π°ΡΠ°",
suggested_action: "Π Π°ΡΡΠΌΠΎΡΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠΎΠΌΠΎ-Π°ΠΊΡΠΈΠΉ ΠΈΠ»ΠΈ ΡΠ½ΠΈΠΆΠ΅Π½ΠΈΡ ΡΠ΅Π½Ρ"
});
}
});
return alerts.sort((a, b) => {
const levelOrder = { "CRITICAL": 3, "WARNING": 2, "INFO": 1 };
return levelOrder[b.level] - levelOrder[a.level];
});
}
private calculateEfficiencyScore(quant: any): number {
const conversionWeight = 0.4;
const viewsWeight = 0.3;
const stockTurnoverWeight = 0.3;
const conversionScore = Math.min(quant.metrics.conversion_rate * 20, 1); // Π½ΠΎΡΠΌΠ°Π»ΠΈΠ·Π°ΡΠΈΡ Π΄ΠΎ 1
const viewsScore = Math.min(quant.metrics.views / 1000, 1); // Π½ΠΎΡΠΌΠ°Π»ΠΈΠ·Π°ΡΠΈΡ Π΄ΠΎ 1
const turnoverScore = quant.stock.quantity > 0 ?
Math.min(quant.metrics.purchases / quant.stock.quantity, 1) : 0;
return (conversionScore * conversionWeight +
viewsScore * viewsWeight +
turnoverScore * stockTurnoverWeight);
}
}
interface QuantProduct {
quant_code: string;
name: string;
quantity: number;
price: string;
status: string;
visibility: string;
}
interface DetailedQuantInfo {
quant_code: string;
name: string;
quantity: number;
price: string;
stock: {
quantity: number;
available: number;
reserved: number;
};
metrics: {
views: number;
purchases: number;
conversion_rate: number;
};
efficiency_score: number;
profitability_score: number;
market_position: string;
}
interface QuantPerformanceReport {
report_date: string;
total_quants: number;
performance_metrics: PerformanceMetrics;
top_performers: DetailedQuantInfo[];
underperformers: DetailedQuantInfo[];
optimization_recommendations: OptimizationRecommendation[];
inventory_alerts: InventoryAlert[];
}
SmartQuantManager - Π£ΠΌΠ½ΡΠΉ ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ ΠΊΠ²Π°Π½ΡΠΎΠ²
class SmartQuantManager {
constructor(private api: QuantsApi) {}
async optimizeQuantPortfolio(): Promise<OptimizationResult> {
// ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΡΠ΅ΠΊΡΡΠ΅Π³ΠΎ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ ΠΏΠΎΡΡΡΠ΅Π»Ρ
const currentPortfolio = await this.analyzeCurrentPortfolio();
// ΠΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠ΅ΠΉ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠΈ
const opportunities = this.identifyOptimizationOpportunities(currentPortfolio);
// Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΠ»Π°Π½Π° ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠΈ
const optimizationPlan = this.createOptimizationPlan(opportunities);
return {
current_state: currentPortfolio,
optimization_opportunities: opportunities,
recommended_actions: optimizationPlan.actions,
expected_improvements: optimizationPlan.expectedImprovements,
implementation_timeline: optimizationPlan.timeline
};
}
private async analyzeCurrentPortfolio(): Promise<PortfolioAnalysis> {
const allQuants = await this.getAllQuantsWithDetails();
// ΠΠ°ΡΠ΅Π³ΠΎΡΠΈΠ·Π°ΡΠΈΡ ΡΠΎΠ²Π°ΡΠΎΠ² ΠΏΠΎ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ
const categories = this.categorizeByPerformance(allQuants);
// ΠΠ½Π°Π»ΠΈΠ· ΡΠ°ΡΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ ΠΏΠΎ ΡΠ΅Π½ΠΎΠ²ΡΠΌ ΡΠ΅Π³ΠΌΠ΅Π½ΡΠ°ΠΌ
const priceSegments = this.analyzePriceSegments(allQuants);
// ΠΠ½Π°Π»ΠΈΠ· ΠΎΠ±ΠΎΡΠ°ΡΠΈΠ²Π°Π΅ΠΌΠΎΡΡΠΈ
const turnoverAnalysis = this.analyzeTurnover(allQuants);
return {
total_quants: allQuants.length,
performance_categories: categories,
price_segments: priceSegments,
turnover_analysis: turnoverAnalysis,
health_score: this.calculatePortfolioHealthScore(allQuants)
};
}
private categorizeByPerformance(quants: DetailedQuantInfo[]): PerformanceCategories {
const stars = quants.filter(q => q.efficiency_score > 0.8 && q.metrics.conversion_rate > 0.03);
const cashCows = quants.filter(q => q.efficiency_score > 0.6 && q.metrics.purchases > 50);
const questionMarks = quants.filter(q => q.efficiency_score > 0.4 && q.metrics.views > 500 && q.metrics.purchases < 20);
const dogs = quants.filter(q => q.efficiency_score < 0.3 && q.metrics.conversion_rate < 0.01);
return {
stars: { count: stars.length, items: stars },
cash_cows: { count: cashCows.length, items: cashCows },
question_marks: { count: questionMarks.length, items: questionMarks },
dogs: { count: dogs.length, items: dogs }
};
}
private identifyOptimizationOpportunities(portfolio: PortfolioAnalysis): OptimizationOpportunity[] {
const opportunities: OptimizationOpportunity[] = [];
// ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ Π΄Π»Ρ Π·Π²Π΅Π·Π΄ (Π²ΡΡΠΎΠΊΠΎΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΡΠ΅ ΡΠΎΠ²Π°ΡΡ)
portfolio.performance_categories.stars.items.forEach(star => {
if (star.stock.available < 20) {
opportunities.push({
type: "INCREASE_INVENTORY",
quant_code: star.quant_code,
priority: "HIGH",
rationale: "ΠΡΡΠΎΠΊΠΎΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΡΠΉ ΡΠΎΠ²Π°Ρ Ρ Π½ΠΈΠ·ΠΊΠΈΠΌ ΠΎΡΡΠ°ΡΠΊΠΎΠΌ",
expected_revenue_increase: star.metrics.purchases * parseFloat(star.price) * 0.3
});
}
});
// ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ Π΄Π»Ρ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ½ΡΡ
ΡΠΎΠ²Π°ΡΠΎΠ²
portfolio.performance_categories.dogs.items.forEach(dog => {
if (dog.stock.available > 50) {
opportunities.push({
type: "REDUCE_PRICE_OR_LIQUIDATE",
quant_code: dog.quant_code,
priority: "MEDIUM",
rationale: "ΠΠ΅ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΡΠΉ ΡΠΎΠ²Π°Ρ Ρ ΠΈΠ·Π±ΡΡΠΎΡΠ½ΡΠΌ ΠΎΡΡΠ°ΡΠΊΠΎΠΌ",
expected_cost_savings: dog.stock.available * parseFloat(dog.price) * 0.1 // ΡΠΊΠΎΠ½ΠΎΠΌΠΈΡ Π½Π° Ρ
ΡΠ°Π½Π΅Π½ΠΈΠΈ
});
}
});
// ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ Π΄Π»Ρ "Π·Π½Π°ΠΊΠΎΠ² Π²ΠΎΠΏΡΠΎΡΠ°"
portfolio.performance_categories.question_marks.items.forEach(questionMark => {
opportunities.push({
type: "TEST_PRICE_REDUCTION",
quant_code: questionMark.quant_code,
priority: "MEDIUM",
rationale: "ΠΡΡΠΎΠΊΠΈΠΉ ΡΡΠ°ΡΠΈΠΊ, Π½ΠΈΠ·ΠΊΠ°Ρ ΠΊΠΎΠ½Π²Π΅ΡΡΠΈΡ - Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ° Ρ ΡΠ΅Π½ΠΎΠΉ",
expected_conversion_increase: 0.02 // ΡΠ²Π΅Π»ΠΈΡΠ΅Π½ΠΈΠ΅ ΠΊΠΎΠ½Π²Π΅ΡΡΠΈΠΈ Π½Π° 2%
});
});
return opportunities.sort((a, b) => {
const priorityOrder = { "HIGH": 3, "MEDIUM": 2, "LOW": 1 };
return priorityOrder[b.priority] - priorityOrder[a.priority];
});
}
}
interface OptimizationOpportunity {
type: "INCREASE_INVENTORY" | "REDUCE_PRICE_OR_LIQUIDATE" | "TEST_PRICE_REDUCTION" | "IMPROVE_VISIBILITY";
quant_code: string;
priority: "HIGH" | "MEDIUM" | "LOW";
rationale: string;
expected_revenue_increase?: number;
expected_cost_savings?: number;
expected_conversion_increase?: number;
}
interface PortfolioAnalysis {
total_quants: number;
performance_categories: PerformanceCategories;
price_segments: PriceSegmentAnalysis;
turnover_analysis: TurnoverAnalysis;
health_score: number;
}
ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ
try {
const quantInfo = await quantsApi.getInfo({
quant_code: ['INVALID_CODE']
});
} catch (error) {
if (error.response?.status === 404) {
console.error("ΠΠΊΠΎΠ½ΠΎΠΌ-ΡΠΎΠ²Π°ΡΡ Ρ ΡΠΊΠ°Π·Π°Π½Π½ΡΠΌΠΈ ΠΊΠΎΠ΄Π°ΠΌΠΈ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ");
} else if (error.response?.status === 400) {
console.error("ΠΠ΅ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ Π·Π°ΠΏΡΠΎΡΠ°:", error.response.data);
} else {
console.error("ΠΠ΅ΠΎΠΆΠΈΠ΄Π°Π½Π½Π°Ρ ΠΎΡΠΈΠ±ΠΊΠ°:", error.message);
}
}
ΠΡΡΡΠΈΠ΅ ΠΏΡΠ°ΠΊΡΠΈΠΊΠΈ
ΠΡΡΠ΅ΠΊΡΠΈΠ²Π½Π°Ρ ΡΠ°Π±ΠΎΡΠ° Ρ ΠΏΠ°Π³ΠΈΠ½Π°ΡΠΈΠ΅ΠΉ
async function getAllQuantsEfficiently(): Promise<QuantProduct[]> {
const allQuants: QuantProduct[] = [];
let cursor: string | undefined;
let batchCount = 0;
do {
const response = await quantsApi.getList({
limit: 500, // ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΡΠΉ ΡΠ°Π·ΠΌΠ΅Ρ Π±Π°ΡΡΠ°
cursor
});
allQuants.push(...response.products);
cursor = response.cursor;
batchCount++;
// ΠΠΎΠ³ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΡΠΎΠ³ΡΠ΅ΡΡΠ° ΠΊΠ°ΠΆΠ΄ΡΠ΅ 10 Π±Π°ΡΡΠ΅ΠΉ
if (batchCount % 10 === 0) {
console.log(`ΠΠ°Π³ΡΡΠΆΠ΅Π½ΠΎ ${allQuants.length} ΡΠΎΠ²Π°ΡΠΎΠ² Π² ${batchCount} Π±Π°ΡΡΠ°Ρ
`);
}
// Rate limiting Π΄Π»Ρ Π±ΠΎΠ»ΡΡΠΈΡ
ΠΎΠ±ΡΠ΅ΠΌΠΎΠ² Π΄Π°Π½Π½ΡΡ
if (batchCount > 5) {
await new Promise(resolve => setTimeout(resolve, 50));
}
} while (cursor);
return allQuants;
}
ΠΠΎΠ½ΠΈΡΠΎΡΠΈΠ½Π³ ΠΊΡΠΈΡΠΈΡΠ΅ΡΠΊΠΈΡ ΠΌΠ΅ΡΡΠΈΠΊ
interface QuantHealthCheck {
total_active: number;
out_of_stock: number;
low_conversion: number;
high_performers: number;
health_score: number;
}
async function performHealthCheck(): Promise<QuantHealthCheck> {
const activeQuants = await quantsApi.getList({
status: 'ACTIVE',
visibility: 'IN_SALE',
limit: 1000
});
const outOfStock = activeQuants.products.filter(q => q.stock.available === 0).length;
const codes = activeQuants.products.map(q => q.quant_code);
const detailedInfo = await quantsApi.getInfo({ quant_code: codes.slice(0, 100) }); // ΠΏΡΠΈΠΌΠ΅Ρ Π΄Π»Ρ ΠΏΠ΅ΡΠ²ΡΡ
100
const lowConversion = detailedInfo.items.filter(q => q.metrics.conversion_rate < 0.01).length;
const highPerformers = detailedInfo.items.filter(q => q.metrics.conversion_rate > 0.05).length;
const healthScore = Math.max(0, Math.min(100,
100 - (outOfStock * 2) - (lowConversion * 1) + (highPerformers * 0.5)
));
return {
total_active: activeQuants.products.length,
out_of_stock: outOfStock,
low_conversion: lowConversion,
high_performers: highPerformers,
health_score: Math.round(healthScore)
};
}
ΠΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΠΎΠ½Π½ΡΠ΅ Π·Π°ΠΌΠ΅ΡΠΊΠΈ
- Rate Limiting: API ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ Π΄ΠΎ 100 Π·Π°ΠΏΡΠΎΡΠΎΠ² Π² ΠΌΠΈΠ½ΡΡΡ
- Batch Processing: Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΡΡΡ Π·Π°ΠΏΡΠ°ΡΠΈΠ²Π°ΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ Π±Π°ΡΡΠ°ΠΌΠΈ Π΄ΠΎ 50 ΠΊΠΎΠ΄ΠΎΠ²
- Cursor Pagination: ΠΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ ΠΊΡΡΡΠΎΡΠ½Π°Ρ ΠΏΠ°Π³ΠΈΠ½Π°ΡΠΈΡ Π΄Π»Ρ ΡΡΡΠ΅ΠΊΡΠΈΠ²Π½ΠΎΠΉ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ
- Status Management: Π‘ΡΠ°ΡΡΡΡ ΡΠΎΠ²Π°ΡΠΎΠ² ΠΎΠ±Π½ΠΎΠ²Π»ΡΡΡΡΡ Π² ΡΠ΅Π°Π»ΡΠ½ΠΎΠΌ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ
- Stock Synchronization: ΠΡΡΠ°ΡΠΊΠΈ ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·ΠΈΡΡΡΡΡΡ Ρ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΡΠΈΡΡΠ΅ΠΌΠΎΠΉ ΡΡΠ΅ΡΠ°
- Performance Metrics: ΠΠ΅ΡΡΠΈΠΊΠΈ ΠΎΠ±Π½ΠΎΠ²Π»ΡΡΡΡΡ Ρ Π·Π°Π΄Π΅ΡΠΆΠΊΠΎΠΉ Π΄ΠΎ 24 ΡΠ°ΡΠΎΠ²