TimesFM Forecasting
google-research/timesfm
Forecasts any univariate time series zero-shot with Google's TimesFM model, returning point forecasts and calibrated prediction intervals without training.
Election forecasting models, campaign analysis, coalition prediction, voter behavior analysis for Swedish elections
$ npx skills add Hack23/cia --skill electoral-analysis -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install Hack23/cia electoral-analysis --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/Hack23/cia.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/electoral-analysis .claude/skills/electoral-analysis && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "electoral-analysis" agent skill from https://github.com/Hack23/cia/tree/master/.github/skills/electoral-analysis into .claude/skills/electoral-analysis/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "electoral-analysis", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/Hack23/cia/tree/master/.github/skills/electoral-analysisType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add Hack23/cia --skill electoral-analysis -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install Hack23/cia electoral-analysis --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Hack23/cia.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.github/skills/electoral-analysis .agents/skills/electoral-analysis && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "electoral-analysis" agent skill from https://github.com/Hack23/cia/tree/master/.github/skills/electoral-analysis into .agents/skills/electoral-analysis/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "electoral-analysis", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Hack23/cia --skill electoral-analysis -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install Hack23/cia electoral-analysis --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Hack23/cia.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.github/skills/electoral-analysis .cursor/skills/electoral-analysis && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "electoral-analysis" agent skill from https://github.com/Hack23/cia/tree/master/.github/skills/electoral-analysis into .cursor/skills/electoral-analysis/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "electoral-analysis", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/Hack23/cia.git --path .github/skills/electoral-analysis--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add Hack23/cia --skill electoral-analysis -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install Hack23/cia electoral-analysis --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Hack23/cia.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.github/skills/electoral-analysis .gemini/skills/electoral-analysis && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "electoral-analysis" agent skill from https://github.com/Hack23/cia/tree/master/.github/skills/electoral-analysis into .gemini/skills/electoral-analysis/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "electoral-analysis", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install Hack23/cia electoral-analysisInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add Hack23/cia --skill electoral-analysis -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/Hack23/cia.git skills-src && mkdir -p .github/skills && cp -r skills-src/.github/skills/electoral-analysis .github/skills/electoral-analysis && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "electoral-analysis" agent skill from https://github.com/Hack23/cia/tree/master/.github/skills/electoral-analysis into .github/skills/electoral-analysis/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "electoral-analysis", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Hack23/cia --skill electoral-analysis -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install Hack23/cia electoral-analysis --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/Hack23/cia.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.github/skills/electoral-analysis .opencode/skills/electoral-analysis && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "electoral-analysis" agent skill from https://github.com/Hack23/cia/tree/master/.github/skills/electoral-analysis into .opencode/skills/electoral-analysis/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "electoral-analysis", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
electoral-analysisElection forecasting models, campaign analysis, coalition prediction, voter behavior analysis for Swedish elections
Electoral Analysis is an agent skill from Hack23/cia. Election forecasting models, campaign analysis, coalition prediction, voter behavior analysis for Swedish elections
Its SKILL.md is about 6.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Data & Analytics, covering Forecasting and time series. The repository describes itself as: Citizen Intelligence Agency. Open-source intelligence platform analyzing Swedish political activities using AI and data visualization. Tracks politicians, government… The licence is Apache-2.0.
5 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit bbed538. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are python, sql and mermaid).
From the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
github.comval.seriksdagen.senovus.sekantarsifo.seyougov.sedemoskop.seFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Electoral Analysis loads about 6.5k tokens when it runs. Until then it costs about 34 tokens; SKILL.md has 456 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from Hack23/cia at commit bbed538, republished under its Apache-2.0 licence (© Hack23). 456 words, ~6,492 tokens.
.claude/skills/electoral-analysis/SKILL.md (or your agent's skills folder).This skill provides comprehensive methodologies for analyzing Swedish electoral dynamics, forecasting election outcomes, predicting coalition formations, and assessing campaign effectiveness. It integrates statistical modeling, polling analysis, and historical trend analysis to produce high-confidence intelligence products for democratic accountability assessment.
Apply this skill when:
Do NOT use for:
graph TB
subgraph "Electoral Framework"
A[349 Riksdag Seats]
A --> B[310 Constituency Seats<br/>29 constituencies]
A --> C[39 Leveling Seats<br/>National proportionality]
end
subgraph "Allocation Rules"
D[Modified Sainte-Laguë]
E[4% National Threshold]
F[12% Constituency Threshold]
D --> G[Seat Distribution]
E --> G
F --> G
end
subgraph "Electoral Cycle"
H[4-Year Fixed Term]
I[September Elections]
J[Sunday Voting]
H & I & J --> K[Election Day 2026<br/>September 13]
end
subgraph "Forecasting Inputs"
L[Historical Results<br/>1970-2022]
M[Opinion Polls<br/>Monthly tracking]
N[Demographic Shifts]
O[Campaign Events]
L & M & N & O --> P[Election Model]
end
P --> Q[Seat Projections]
P --> R[Coalition Scenarios]
style A fill:#e1f5ff
style D fill:#ffeb99
style P fill:#ffe6cc
style Q fill:#ccffcc
style R fill:#ccffccPurpose: Combine multiple polls to estimate current vote intention with confidence intervals.
import pandas as pd
import numpy as np
from scipy import stats
from datetime import datetime, timedelta
class SwedishElectionForecaster:
"""
Electoral forecasting for Swedish Riksdag elections
Supports: Predictive Intelligence Framework
"""
def __init__(self, db_connection):
self.db = db_connection
self.parties = ['S', 'M', 'SD', 'C', 'V', 'KD', 'L', 'MP']
self.threshold = 4.0 # Electoral threshold
def aggregate_polls_weighted(self, lookback_days=90):
"""
Weighted poll aggregation using recency and sample size
Data Source: External polling data (Novus, Sifo, YouGov, Demoskop)
Intelligence Product: Current vote intention estimates
"""
# Query: Fetch recent polls
query = f"""
SELECT
poll_date,
polling_company,
sample_size,
party,
percentage
FROM opinion_polls
WHERE poll_date >= CURRENT_DATE - INTERVAL '{lookback_days} days'
ORDER BY poll_date DESC
"""
df = pd.read_sql(query, self.db)
# Calculate weights
df['days_ago'] = (pd.Timestamp.now() - pd.to_datetime(df['poll_date'])).dt.days
df['recency_weight'] = np.exp(-df['days_ago'] / 30) # Exponential decay, half-life 30 days
df['sample_weight'] = np.sqrt(df['sample_size']) / 1000 # Sample size adjustment
df['total_weight'] = df['recency_weight'] * df['sample_weight']
# Weighted average by party
aggregated = df.groupby('party').apply(
lambda x: np.average(x['percentage'], weights=x['total_weight'])
).to_dict()
# Calculate standard errors
standard_errors = df.groupby('party').apply(
lambda x: np.sqrt(np.average((x['percentage'] - aggregated[x.name])**2, weights=x['total_weight']))
).to_dict()
# 95% confidence intervals
confidence_intervals = {
party: {
'estimate': aggregated[party],
'lower_95': aggregated[party] - 1.96 * standard_errors[party],
'upper_95': aggregated[party] + 1.96 * standard_errors[party]
}
for party in self.parties
}
return confidence_intervals
def structural_forecast_model(self, election_date):
"""
Structural model combining polls, fundamentals, and historical patterns
Model Components:
1. Current polling average (weighted 50%)
2. Economic indicators (weighted 25%)
3. Incumbency advantage/disadvantage (weighted 15%)
4. Campaign effects (weighted 10%)
"""
# Component 1: Polling average
polls = self.aggregate_polls_weighted()
# Component 2: Economic fundamentals
query = """
SELECT
indicator_name,
value,
year
FROM world_bank_data
WHERE country_code = 'SWE'
AND indicator_name IN ('GDP growth', 'Unemployment rate', 'Inflation')
AND year = EXTRACT(YEAR FROM CURRENT_DATE) - 1
"""
economic_df = pd.read_sql(query, self.db)
economic_score = self.calculate_economic_vote(economic_df)
# Component 3: Incumbency factor
query_incumbent = """
SELECT
party,
in_government,
government_duration_years
FROM current_government_status
"""
incumbency_df = pd.read_sql(query_incumbent, self.db)
incumbency_effects = self.calculate_incumbency_penalty(incumbency_df)
# Component 4: Campaign effects (closer to election = more weight on polls)
days_until_election = (election_date - datetime.now()).days
campaign_factor = 1.0 if days_until_election < 30 else 0.5 # Polls more reliable near election
# Combine components
forecasts = {}
for party in self.parties:
poll_component = polls[party]['estimate'] * 0.5 * campaign_factor
economic_component = economic_score.get(party, 0) * 0.25
incumbency_component = incumbency_effects.get(party, 0) * 0.15
forecast = poll_component + economic_component + incumbency_component
# Ensure non-negative and sums to 100%
forecasts[party] = max(0, forecast)
# Normalize to 100%
total = sum(forecasts.values())
forecasts = {party: (vote / total) * 100 for party, vote in forecasts.items()}
return forecasts
def calculate_economic_vote(self, economic_df):
"""
Model economic voting: Good economy benefits incumbents
Formula: ΔVote = β₁*GDP_growth + β₂*Unemployment_change + β₃*Inflation
Coefficients based on Swedish electoral research
"""
gdp_growth = economic_df[economic_df['indicator_name'] == 'GDP growth']['value'].iloc[0]
unemployment = economic_df[economic_df['indicator_name'] == 'Unemployment rate']['value'].iloc[0]
inflation = economic_df[economic_df['indicator_name'] == 'Inflation']['value'].iloc[0]
# Economic vote model (simplified coefficients)
economic_advantage = (0.5 * gdp_growth) - (0.3 * unemployment) - (0.2 * inflation)
# Query: Which parties are in government
query = "SELECT party FROM current_government_status WHERE in_government = TRUE"
incumbent_parties = pd.read_sql(query, self.db)['party'].tolist()
# Allocate economic vote to incumbents
economic_scores = {}
for party in self.parties:
if party in incumbent_parties:
economic_scores[party] = economic_advantage / len(incumbent_parties)
else:
economic_scores[party] = 0
return economic_scores
def calculate_incumbency_penalty(self, incumbency_df):
"""
Model incumbency fatigue: Long-serving governments lose support
Penalty = -0.5% per year in government (capped at -5%)
"""
penalties = {}
for _, row in incumbency_df.iterrows():
if row['in_government']:
penalty = min(-0.5 * row['government_duration_years'], -5.0)
penalties[row['party']] = penalty
else:
penalties[row['party']] = 0
return penaltiesPurpose: Convert vote share forecasts to seat allocations using Modified Sainte-Laguë method.
def project_riksdag_seats(self, vote_shares):
"""
Project Riksdag seat distribution from vote share forecasts
Method: Modified Sainte-Laguë with 4% threshold
Output: 349 seats allocated across parties
"""
# Apply 4% threshold
qualified_parties = {
party: vote for party, vote in vote_shares.items()
if vote >= self.threshold
}
if len(qualified_parties) == 0:
raise ValueError("No parties exceed 4% threshold")
# Allocate 349 seats
seats_allocated = {party: 0 for party in qualified_parties}
for seat_num in range(349):
# Calculate quotient for each party
quotients = {}
for party, vote_pct in qualified_parties.items():
if seats_allocated[party] == 0:
divisor = 1.4 # First seat divisor (modified Sainte-Laguë)
else:
divisor = 2 * seats_allocated[party] + 1
quotients[party] = vote_pct / divisor
# Award seat to party with highest quotient
winning_party = max(quotients, key=quotients.get)
seats_allocated[winning_party] += 1
return seats_allocated
def monte_carlo_seat_simulation(self, vote_forecasts, n_simulations=10000):
"""
Monte Carlo simulation for seat projection confidence intervals
Method: Sample from vote share distributions, calculate seats
Output: Probability distribution of seat outcomes
"""
seat_simulations = {party: [] for party in self.parties}
for _ in range(n_simulations):
# Sample vote shares from normal distributions (using forecast uncertainties)
sampled_votes = {}
for party in self.parties:
mean = vote_forecasts[party]['estimate']
std = (vote_forecasts[party]['upper_95'] - vote_forecasts[party]['lower_95']) / (2 * 1.96)
# Sample and ensure non-negative
sampled_votes[party] = max(0, np.random.normal(mean, std))
# Normalize to 100%
total = sum(sampled_votes.values())
sampled_votes = {party: (vote / total) * 100 for party, vote in sampled_votes.items()}
# Calculate seats for this sample
try:
seats = self.project_riksdag_seats(sampled_votes)
for party in self.parties:
seat_simulations[party].append(seats.get(party, 0))
except ValueError:
# Skip if no parties exceed threshold (rare edge case)
continue
# Calculate statistics
seat_projections = {}
for party in self.parties:
sims = seat_simulations[party]
seat_projections[party] = {
'median': int(np.median(sims)),
'mean': np.mean(sims),
'lower_95': int(np.percentile(sims, 2.5)),
'upper_95': int(np.percentile(sims, 97.5)),
'probability_in_riksdag': sum(s > 0 for s in sims) / len(sims)
}
return seat_projectionsPurpose: Forecast which coalition is most likely to form government post-election.
class CoalitionPredictor:
"""
Coalition formation analysis using game theory and historical patterns
Supports: Decision Intelligence Framework
"""
def __init__(self, db_connection):
self.db = db_connection
def enumerate_viable_coalitions(self, seat_projections):
"""
Generate all mathematically viable coalition combinations
Criteria:
1. Total seats ≥ 175 (majority)
2. Ideologically compatible parties
3. No historical vetoes (e.g., no party wants coalition with SD except M/KD)
"""
from itertools import combinations
parties = list(seat_projections.keys())
viable_coalitions = []
# Define compatibility matrix based on Swedish political reality
incompatible_pairs = [
('S', 'M'), # Polar opposites
('S', 'SD'), # S refuses SD cooperation
('V', 'M'), # Ideological incompatibility
('V', 'KD'), # Ideological incompatibility
('MP', 'SD'), # Ideological incompatibility
('L', 'V'), # Ideological distance
]
# Iterate through all possible combinations
for r in range(1, len(parties) + 1):
for combo in combinations(parties, r):
total_seats = sum(seat_projections[p]['median'] for p in combo)
# Check majority threshold
if total_seats >= 175:
# Check compatibility
compatible = True
for p1, p2 in combinations(combo, 2):
if (p1, p2) in incompatible_pairs or (p2, p1) in incompatible_pairs:
compatible = False
break
if compatible:
viable_coalitions.append({
'parties': combo,
'total_seats': total_seats,
'size': len(combo)
})
return viable_coalitions
def calculate_coalition_stability(self, coalition_parties):
"""
Assess coalition stability using voting alignment history
Data Source: view_riksdagen_party_coalition_agreeableness
Output: Stability score 0-100
"""
query = f"""
SELECT
p1.party as party_a,
p2.party as party_b,
AVG(CASE WHEN p1.party_position = p2.party_position THEN 1.0 ELSE 0.0 END) as alignment_rate
FROM view_riksdagen_party_ballot_support_annual_summary p1
JOIN view_riksdagen_party_ballot_support_annual_summary p2
ON p1.ballot_id = p2.ballot_id
AND p1.party < p2.party
WHERE p1.party IN {tuple(coalition_parties)}
AND p2.party IN {tuple(coalition_parties)}
AND p1.vote_date >= CURRENT_DATE - INTERVAL '4 years'
GROUP BY p1.party, p2.party
"""
alignment_df = pd.read_sql(query, self.db)
# Average pairwise alignment
stability_score = alignment_df['alignment_rate'].mean() * 100
return stability_score
def predict_coalition_probability(self, viable_coalitions):
"""
Assign formation probability to each viable coalition
Factors:
1. Seat surplus (more seats = more stable)
2. Coalition size (fewer parties = easier negotiation)
3. Historical stability (voting alignment)
4. Ideological cohesion
"""
coalition_scores = []
for coalition in viable_coalitions:
parties = coalition['parties']
seats = coalition['total_seats']
size = coalition['size']
# Factor 1: Seat surplus (above 175)
seat_surplus = seats - 175
seat_score = min(seat_surplus / 50, 1.0) * 30 # Max 30 points
# Factor 2: Coalition size (fewer is better)
size_score = max(0, 30 - (size - 1) * 10) # 30 for single party, 20 for 2 parties, etc.
# Factor 3: Historical stability
stability = self.calculate_coalition_stability(parties)
stability_score = stability * 0.3 # Max 30 points
# Factor 4: Ideological cohesion (simplified)
# Center-right bloc: M, KD, L, C = high cohesion
# Left bloc: S, V, MP = high cohesion
if set(parties).issubset({'M', 'KD', 'L', 'C'}):
ideology_score = 10
elif set(parties).issubset({'S', 'V', 'MP'}):
ideology_score = 10
else:
ideology_score = 5 # Mixed bloc
total_score = seat_score + size_score + stability_score + ideology_score
coalition_scores.append({
'coalition': ' + '.join(parties),
'parties': parties,
'seats': seats,
'probability_score': total_score,
'factors': {
'seat_surplus': seat_score,
'size_penalty': size_score,
'stability': stability_score,
'ideology': ideology_score
}
})
# Normalize scores to probabilities
total_score = sum(c['probability_score'] for c in coalition_scores)
for coalition in coalition_scores:
coalition['formation_probability'] = (coalition['probability_score'] / total_score) * 100
# Sort by probability
coalition_scores.sort(key=lambda x: x['formation_probability'], reverse=True)
return coalition_scoresPurpose: Identify and model voters likely to switch parties between elections.
-- Swing District Analysis: Identify constituencies with high volatility
WITH election_volatility AS (
SELECT
constituency_name,
election_year,
party_name,
percentage,
ABS(percentage - LAG(percentage) OVER (
PARTITION BY constituency_name, party_name
ORDER BY election_year
)) as vote_swing
FROM constituency_election_results
WHERE election_year >= 2010
),
constituency_volatility_score AS (
SELECT
constituency_name,
AVG(vote_swing) as avg_swing,
MAX(vote_swing) as max_swing,
STDDEV(vote_swing) as swing_volatility
FROM election_volatility
WHERE vote_swing IS NOT NULL
GROUP BY constituency_name
)
SELECT
constituency_name,
ROUND(avg_swing, 2) as avg_swing_pct,
ROUND(max_swing, 2) as max_swing_pct,
ROUND(swing_volatility, 2) as volatility,
CASE
WHEN avg_swing > 5.0 THEN 'HIGH VOLATILITY - Swing District'
WHEN avg_swing > 3.0 THEN 'MODERATE VOLATILITY'
ELSE 'LOW VOLATILITY - Safe District'
END as district_classification
FROM constituency_volatility_score
ORDER BY avg_swing DESC
LIMIT 20;Purpose: Measure impact of campaign events on polling and vote intention.
def analyze_campaign_event_impact(self, event_date, event_description):
"""
Interrupted time series analysis for campaign event impact
Method: Compare polling trend before/after event
Example Events: Leader debates, scandals, policy announcements
"""
# Query: Polling data 60 days before and after event
query = f"""
SELECT
poll_date,
party,
percentage
FROM opinion_polls
WHERE poll_date BETWEEN '{event_date - timedelta(days=60)}'
AND '{event_date + timedelta(days=60)}'
ORDER BY poll_date
"""
df = pd.read_sql(query, self.db)
# Create intervention variable
df['post_event'] = (df['poll_date'] > event_date).astype(int)
df['days_since_start'] = (df['poll_date'] - df['poll_date'].min()).dt.days
impact_results = {}
for party in df['party'].unique():
party_df = df[df['party'] == party].copy()
# Fit regression: Vote% ~ Time + Post_Event + Time*Post_Event
from sklearn.linear_model import LinearRegression
X = party_df[['days_since_start', 'post_event']]
X['interaction'] = X['days_since_start'] * X['post_event']
y = party_df['percentage']
model = LinearRegression()
model.fit(X, y)
# Extract coefficients
time_trend = model.coef_[0]
event_impact = model.coef_[1]
trend_change = model.coef_[2]
impact_results[party] = {
'event': event_description,
'immediate_impact': event_impact, # Jump in support at event
'trend_change': trend_change, # Change in trend slope
'statistical_significance': self._calculate_p_value(model, X, y)
}
return impact_resultsPurpose: Monitor parties at risk of falling below 4% threshold.
-- Threshold Risk Analysis: Parties near 4% cutoff
WITH recent_polls AS (
SELECT
party,
poll_date,
percentage,
ROW_NUMBER() OVER (PARTITION BY party ORDER BY poll_date DESC) as recency_rank
FROM opinion_polls
WHERE poll_date >= CURRENT_DATE - INTERVAL '90 days'
),
threshold_analysis AS (
SELECT
party,
AVG(percentage) as avg_support,
STDDEV(percentage) as support_volatility,
MIN(percentage) as min_support,
MAX(percentage) as max_support,
COUNT(*) as poll_count
FROM recent_polls
WHERE recency_rank <= 10 -- Last 10 polls per party
GROUP BY party
)
SELECT
party,
ROUND(avg_support, 2) as current_support,
ROUND(support_volatility, 2) as volatility,
ROUND(min_support, 2) as lowest_poll,
ROUND(max_support, 2) as highest_poll,
CASE
WHEN avg_support < 4.0 THEN '🔴 BELOW THRESHOLD - No seats'
WHEN avg_support < 4.5 THEN '🟠 CRITICAL RISK - Within margin of error'
WHEN avg_support < 5.0 THEN '🟡 MODERATE RISK - Close to threshold'
ELSE '🟢 SAFE - Above threshold'
END as threshold_risk,
-- Probability of exceeding threshold (normal distribution assumption)
ROUND(
100 * (1 - stats.norm.cdf(4.0, avg_support, support_volatility)),
1
) as probability_exceeds_threshold
FROM threshold_analysis
WHERE avg_support <= 6.0 -- Focus on at-risk parties
ORDER BY avg_support ASC;A.5.9 - Inventory of Information and Other Associated Assets
A.5.33 - Protection of Records
IDENTIFY (ID)
DETECT (DE)
CIS Control 3: Data Protection
CIS Control 12: Network Infrastructure Management
Data Classification Policy
Privacy Policy
AI Policy
Threat Modeling
Official Documentation:
CIA Platform Documentation:
Academic Sources:
Polling Organizations:
© Hack23, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .github/skills/electoral-analysis of Hack23/cia.
Open the folder on GitHubat commit bbed538
Electoral Analysis next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Electoral Analysis this skillHack23/cia | 239 | — | ~6.5k | Automated safety check: Pass | Apache-2.0 | |
| TimesFM Forecastinggoogle-research/timesfm | 34k | — | ~4.7k | Automated safety check: Pass | Apache-2.0 | |
| Timesfm ForecastingzLanqing/codex-claude-academic-skills | 4.6k | 6 repos | ~7.5k | Automated safety check: Notes | Apache-2.0 | |
| StatsmodelszLanqing/codex-claude-academic-skills | 4.6k | 16 repos | ~4.9k | Automated safety check: Pass | BSD-3-Clause | |
| Alphaear Predictorninehills/skills | 281 | 2 repos | ~531 | Automated safety check: Pass | None | |
| Find Hypertable Candidatestimescale/pg-aiguide | 1.9k | 1 repos | ~2.6k | Automated safety check: Pass | Apache-2.0 |
google-research/timesfm
Forecasts any univariate time series zero-shot with Google's TimesFM model, returning point forecasts and calibrated prediction intervals without training.
zLanqing/codex-claude-academic-skills
Zero-shot time series forecasting with Google's TimesFM foundation model.
zLanqing/codex-claude-academic-skills
Statistical models library for Python. An agent skill from zLanqing/codex-claude-academic-skills.
ninehills/skills
Market prediction skill using Kronos. An agent skill from ninehills/skills.
timescale/pg-aiguide
A skill your agent uses to analyze an existing PostgreSQL database and identify which tables should be converted to Timescale/TimescaleDB hypertables.
arkohut/pensieve
Search the user's local Pensieve screenshot archive by text, app, or time range.
Hack23/cia
WCAG 2.1 AA compliance, ARIA attributes, keyboard navigation, screen reader optimization for accessible political data platforms
Hack23/cia
Advanced chart types, D3.js/Vaadin Charts patterns, political data visualization, time series analysis
Hack23/cia
AI governance, EU AI Act compliance, OWASP LLM security, responsible AI practices for GitHub Copilot agents
Hack23/cia
External API integration patterns, retry logic, circuit breakers, caching, rate limiting for government data APIs
Hack23/cia
AWS CloudWatch metrics, alarms, dashboards, log insights, and application monitoring for the CIA platform
Hack23/cia
AWS security best practices, VPC security, IAM, KMS, CloudTrail, GuardDuty for CIA platform deployment
Categories
Election forecasting models, campaign analysis, coalition prediction, voter behavior analysis for Swedish elections. Electoral Analysis is an agent skill from Hack23/cia.
Electoral Analysis fits situations like: tasks that involve Forecasting and time series.
Run `npx skills add Hack23/cia --skill electoral-analysis -a claude-code`. Or copy the skill folder (.github/skills/electoral-analysis in Hack23/cia) into .claude/skills/electoral-analysis in your project. Claude Code loads it when a task matches its description.
Run `npx skills add Hack23/cia --skill electoral-analysis -a codex`. Or copy the skill folder (.github/skills/electoral-analysis in Hack23/cia) into .agents/skills/electoral-analysis in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add Hack23/cia --skill electoral-analysis -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/electoral-analysis, .gemini/skills/electoral-analysis, .github/skills/electoral-analysis and .opencode/skills/electoral-analysis in your project.
SKILL.md names no scripts, command-line tools or credentials: Electoral Analysis is instructions for the agent only. Our summary lists: Python 3.
SKILL.md names 7 domains. As links in the text: github.com, val.se, riksdagen.se, novus.se, kantarsifo.se, yougov.se and demoskop.se. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Electoral Analysis is published under the Apache-2.0 licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.5k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Electoral Analysis: TimesFM Forecasting (google-research/timesfm, 34k stars), Timesfm Forecasting (zLanqing/codex-claude-academic-skills, 4.6k stars), Statsmodels (zLanqing/codex-claude-academic-skills, 4.6k stars) and Alphaear Predictor (ninehills/skills, 281 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Hack23 (a GitHub organization) maintains it in Hack23/cia, which has 239 GitHub stars. The repository holds 78 skills in this directory. The repository was last updated on October 7, 2026.
Source: Hack23/cia on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.