- name
- marketing-science-writing
- description
- AI agent skill for writing marketing science academic papers from topic selection through structural modeling, identification, estimation, and full draft assembly.
- triggers
- ["write a marketing science paper","help me design a consumer utility model","identify causal effects in marketing data","design counterfactual simulations for structural model","write a paper for Marketing Science journal","help with BLP demand estimation","design a conjoint analysis study","set up identification strategy with instrumental variables"]
# Marketing Science Academic Writing Skill
> Skill by [ara.so](https://ara.so) — Marketing Skills collection.
A comprehensive skill for AI coding agents to guide researchers through the complete pipeline of writing marketing science academic papers — from topic positioning and consumer utility modeling through identification design, structural estimation, counterfactual simulations, and full manuscript assembly.
Covers 8 flagship marketing journals: **Marketing Science, JMR, JM, JCR** (UTD-24 tier) and **JAMS, IJRM, QME, Marketing Letters**.
## What This Skill Enables
This skill teaches AI agents to help researchers with:
- **Topic Positioning**: Gap analysis, journal selection, contribution framing
- **Consumer Utility Modeling**: Micro-founded demand systems (logit, nested logit, random coefficients)
- **Identification Strategy**: DID, RDD, IV, structural identification for causal inference
- **Estimation Methods**: BLP, GMM, MLE, Bayesian estimation workflows
- **Counterfactual Simulations**: Merger analysis, policy simulations, welfare calculations
- **Experimental Design**: Field experiments, conjoint analysis, A/B testing
- **Full Draft Assembly**: LaTeX manuscript generation with INFORMS formatting
The skill is **domain-specific** for marketing journals, encoding conventions that generic academic writing skills don't cover (e.g., structural model presentation, identification justification, counterfactual design, reviewer expectations).
## Installation
### 1. Clone Repository
```bash
git clone https://github.com/liyuanbo1024/marketing-science-writing.git
cd marketing-science-writing
```
### 2. Install for Your AI Agent
#### OpenCode
```bash
# Linux/macOS
mkdir -p ~/.config/opencode/skills
cp -r . ~/.config/opencode/skills/marketing-science-writing
# Windows PowerShell
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.config\opencode\skills"
Copy-Item -Recurse . "$env:USERPROFILE\.config\opencode\skills\marketing-science-writing"
```
Or add to `~/.config/opencode/opencode.json`:
```json
{
"skills": {
"paths": [
"~/.config/opencode/skills",
"/path/to/marketing-science-writing"
]
}
}
```
#### Claude Code
```bash
# Linux/macOS
mkdir -p ~/.claude/skills
cp -r . ~/.claude/skills/marketing-science-writing
# Windows PowerShell
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.claude\skills"
Copy-Item -Recurse . "$env:USERPROFILE\.claude\skills\marketing-science-writing"
```
#### Cursor / Windsurf / Other Agents
```bash
# Cursor
cp -r . ~/.cursor/skills/marketing-science-writing
# Windsurf
cp -r . ~/.windsurf/skills/marketing-science-writing
# Generic agent with custom skills directory
cp -r . /your/agent/skills/path/marketing-science-writing
```
## The 5-Stage Pipeline
The skill guides agents through a **0-to-Draft Pipeline**:
| Stage | Output | Key File Reference |
|-------|--------|-------------------|
| **1. Topic Positioning** | Gap table, journal choice, contribution list | `references/journal-characteristics.md` |
| **2. Consumer Utility Model** | Utility specification, demand derivation, notation | `references/modeling-conventions.md` |
| **3. Identification & Estimation** | Identification strategy, estimator, specification tests | `references/identification-guide.md`, `references/estimation-guide.md` |
| **4. Counterfactuals & Experiments** | Counterfactual design, conjoint/field experiment | `references/counterfactual-guide.md`, `references/conjoint-analysis.md` |
| **5. Full Draft Assembly** | Complete LaTeX manuscript | `examples/manuscript_template.tex` |
## Usage Patterns
### Pattern 1: Full Pipeline Mode
User says: *"Take me through the full marketing science pipeline for a paper on dynamic pricing in ride-sharing markets."*
Agent workflow:
```python
# Stage 1: Topic Positioning
# Read: references/journal-characteristics.md
# Output: Gap table, recommend Marketing Science or QME
# Ask user: "Confirm target journal: Marketing Science?"
# Stage 2: Consumer Utility Model
# Read: references/modeling-conventions.md
# Design: Random coefficients logit with time-varying price coefficients
# Output: §3 Model section draft with notation table
# Stage 3: Identification
# Read: references/identification-guide.md
# Design: IV strategy using weather shocks + cost shifters
# Output: §4 Identification and Estimation section
# Stage 4: Counterfactuals
# Read: references/counterfactual-guide.md
# Design: Simulate merger, price cap, surge pricing ban
# Output: §5 Counterfactual Results section
# Stage 5: Assembly
# Read: examples/manuscript_template.tex
# Generate: Full LaTeX draft with all sections
```
### Pattern 2: Stage-Specific Help
User says: *"I have my structural model. Help me design the identification strategy."*
Agent action:
1. Read `references/identification-guide.md`
2. Ask user for endogenous variables, available instruments
3. Propose identification strategy (e.g., BLP instruments + cost shifters)
4. Draft §4.1 Identification subsection
### Pattern 3: Code Generation for Estimation
User says: *"Generate Python code for BLP estimation with random coefficients."*
Agent uses `examples/blp_estimation_example.py`:
```python
import numpy as np
import pandas as pd
from scipy.optimize import minimize
from scipy.stats import norm
class BLPModel:
"""
BLP (1995) Random Coefficients Logit Demand Estimation
Environment variables needed:
- DATA_PATH: path to market-level data CSV
"""
def __init__(self, data_path=None):
if data_path is None:
import os
data_path = os.getenv('DATA_PATH', 'data/market_data.csv')
self.data = pd.read_csv(data_path)
def compute_shares(self, delta, sigma, nu):
"""
Compute predicted market shares via simulation
Args:
delta: mean utilities (J×1)
sigma: std dev of random coefficients (K×1)
nu: simulation draws (N×K)
Returns:
shares: predicted shares (J×1)
"""
J = len(delta)
N = nu.shape[0]
# Individual-level utilities
# u_ij = delta_j + sigma · x_j · nu_i
X = self.data[['price', 'horsepower', 'mpg']].values
u = delta[:, None] + (X @ np.diag(sigma)) @ nu.T # J×N
# Choice probabilities
exp_u = np.exp(u)
denom = 1 + exp_u.sum(axis=0) # 1×N
probs = exp_u / denom # J×N
# Average across simulation draws
shares = probs.mean(axis=1)
return shares
def contraction_mapping(self, delta_init, observed_shares, sigma, nu, tol=1e-8, max_iter=1000):
"""
BLP contraction mapping to invert shares → delta
delta^{t+1} = delta^t + log(s_observed) - log(s_predicted)
"""
delta = delta_init.copy()
for iteration in range(max_iter):
s_pred = self.compute_shares(delta, sigma, nu)
delta_new = delta + np.log(observed_shares) - np.log(s_pred)
if np.abs(delta_new - delta).max() < tol:
return delta_new
delta = delta_new
raise ValueError(f"Contraction mapping did not converge in {max_iter} iterations")
def gmm_objective(self, theta, Z, W):
"""
GMM objective: g(theta)' W g(theta)
Args:
theta: [sigma_price, sigma_hp, sigma_mpg]
Z: instruments (T×M matrix)
W: weighting matrix (M×M)
Returns:
GMM objective value
"""
sigma = theta
nu = np.random.randn(500, 3) # 500 simulation draws
# Solve for delta via contraction mapping
observed_shares = self.data['market_share'].values
delta_init = np.log(observed_shares) - np.log(1 - observed_shares.sum())
delta = self.contraction_mapping(delta_init, observed_shares, sigma, nu)
# Compute structural errors: xi = delta - X*beta
X = self.data[['price', 'horsepower', 'mpg']].values
beta = np.linalg.lstsq(X, delta, rcond=None)[0]
xi = delta - X @ beta
# Moment conditions: E[Z'*xi] = 0
moments = Z.T @ xi # M×1
# GMM objective
obj = moments.T @ W @ moments
return obj
def estimate(self, instruments_cols, initial_sigma=None):
"""
Run two-step GMM estimation
Args:
instruments_cols: list of column names in self.data
initial_sigma: starting values for [sigma_price, sigma_hp, sigma_mpg]
Returns:
theta_hat: estimated random coefficient std devs
se: standard errors
"""
Z = self.data[instruments_cols].values
if initial_sigma is None:
initial_sigma = np.array([0.5, 0.5, 0.5])
# Step 1: Identity weighting matrix
W = np.eye(Z.shape[1])
result1 = minimize(self.gmm_objective, initial_sigma, args=(Z, W),
method='Nelder-Mead', options={'maxiter': 100})
theta1 = result1.x
# Step 2: Optimal weighting matrix
# (In practice: estimate Omega = E[Z'*xi*xi'*Z] then W = inv(Omega))
# Simplified here for demonstration
W_optimal = np.eye(Z.shape[1]) # Replace with actual Omega^{-1}
result2 = minimize(self.gmm_objective, theta1, args=(Z, W_optimal),
method='BFGS')
theta_hat = result2.x
# Standard errors (from inverse Hessian)
se = np.sqrt(np.diag(result2.hess_inv))
return theta_hat, se
# Usage example
if __name__ == '__main__':
model = BLPModel() # Reads from $DATA_PATH
# Instruments: BLP instruments (sum of other firms' characteristics)
# + cost shifters (e.g., steel_price, labor_cost)
instruments = ['blp_price_sum', 'blp_hp_sum', 'blp_mpg_sum',
'steel_price', 'labor_cost']
theta_hat, se = model.estimate(instruments)
print("Estimated Random Coefficient Std Devs:")
print(f" σ_price = {theta_hat[0]:.4f} (SE: {se[0]:.4f})")
print(f" σ_hp = {theta_hat[1]:.4f} (SE: {se[1]:.4f})")
print(f" σ_mpg = {theta_hat[2]:.4f} (SE: {se[2]:.4f})")
```
**Key points**:
- Use environment variables for data paths: `os.getenv('DATA_PATH')`
- Contraction mapping to invert shares → mean utilities
- Two-step GMM with optimal weighting matrix
- BLP instruments: sum of rivals' characteristics + cost shifters
### Pattern 4: Counterfactual Simulation
User says: *"Design counterfactual simulations for a merger between Firm A and Firm B."*
Agent reads `references/counterfactual-guide.md` and generates:
```python
def simulate_merger_counterfactual(model, firm_a_products, firm_b_products):
"""
Simulate post-merger equilibrium prices and welfare
Args:
model: estimated BLPModel instance
firm_a_products: list of product indices owned by Firm A
firm_b_products: list of product indices owned by Firm B
Returns:
results: dict with pre/post prices, quantities, consumer surplus, profits
"""
# Pre-merger: Solve for Nash equilibrium prices
pre_prices = solve_bertrand_equilibrium(model, ownership_matrix_pre)
pre_shares = model.compute_shares(model.delta, model.sigma, model.nu)
pre_cs = compute_consumer_surplus(model, pre_prices)
pre_profits = compute_profits(model, pre_prices, pre_shares, ownership_matrix_pre)
# Post-merger: Update ownership matrix
ownership_matrix_post = ownership_matrix_pre.copy()
# Merge Firm A and Firm B
for i in firm_a_products:
for j in firm_b_products:
ownership_matrix_post[i, j] = 1
ownership_matrix_post[j, i] = 1
# Solve for new Nash equilibrium
post_prices = solve_bertrand_equilibrium(model, ownership_matrix_post)
post_shares = model.compute_shares(
model.delta + model.beta_price * (post_prices - pre_prices),
model.sigma, model.nu
)
post_cs = compute_consumer_surplus(model, post_prices)
post_profits = compute_profits(model, post_prices, post_shares, ownership_matrix_post)
return {
'pre_prices': pre_prices,
'post_prices': post_prices,
'price_change_pct': (post_prices - pre_prices) / pre_prices * 100,
'consumer_surplus_change': post_cs - pre_cs,
'profit_change': post_profits - pre_profits,
'total_welfare_change': (post_cs - pre_cs) + (post_profits - pre_profits)
}
def solve_bertrand_equilibrium(model, ownership, tol=1e-6, max_iter=1000):
"""
Solve for Bertrand-Nash equilibrium prices given ownership structure
"""
prices = model.data['price'].values.copy()
marginal_costs = model.data['marginal_cost'].values
for iteration in range(max_iter):
# Compute demand elasticities
shares = model.compute_shares(model.delta, model.sigma, model.nu)
elasticities = compute_elasticity_matrix(model, prices)
# First-order conditions: p_j - mc_j = -s_j / (∂s_j/∂p_j) [single-product firm]
# Multi-product firm: (P - MC) = -Δ^{-1} * s, where Δ_jk = ∂s_j/∂p_k * ownership_jk
Delta = elasticities * ownership
prices_new = marginal_costs - np.linalg.solve(Delta, shares)
Ver no GitHub