Marek4321 commited on
Commit
f2bd280
·
verified ·
1 Parent(s): 583dfe9

Upload strategy_core.py

Browse files
Files changed (1) hide show
  1. strategy_core.py +271 -0
strategy_core.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Strategic Sandbox - Core Logic Module
3
+ Data models and simulation engine for strategy evaluation
4
+ """
5
+
6
+ import json
7
+ from typing import List, Dict, Any, Optional
8
+ from dataclasses import dataclass, asdict
9
+ import pandas as pd
10
+
11
+
12
+ @dataclass
13
+ class Goal:
14
+ """Strategic goal definition with main metric"""
15
+ text: str
16
+ metric: str
17
+ baseline: float
18
+ target: float
19
+ horizon: str
20
+ unit: str = "%"
21
+
22
+
23
+ @dataclass
24
+ class Arena:
25
+ """Market arena definition"""
26
+ market: str
27
+ category: str
28
+ competitors: List[str]
29
+ target_audience: str = ""
30
+
31
+
32
+ @dataclass
33
+ class Insight:
34
+ """Market or consumer insight"""
35
+ id: str
36
+ text: str
37
+ evidence: List[str]
38
+
39
+
40
+ @dataclass
41
+ class Hypothesis:
42
+ """Testable hypothesis"""
43
+ id: str
44
+ text: str
45
+ based_on: List[str] # insight IDs
46
+ metric: str
47
+ expected_change: float
48
+
49
+
50
+ @dataclass
51
+ class Move:
52
+ """Strategic move/action"""
53
+ id: str
54
+ text: str
55
+ linked_hypothesis: str
56
+ impact: float # 0-1
57
+ fit: float # 0-1
58
+ risk: float # 0-1
59
+ cost: float
60
+
61
+
62
+ @dataclass
63
+ class Metric:
64
+ """Success metric"""
65
+ id: str
66
+ text: str
67
+ baseline: float
68
+ target: float
69
+ unit: str
70
+
71
+
72
+ class Strategy:
73
+ """Complete strategy model"""
74
+
75
+ def __init__(self):
76
+ self.goal: Optional[Goal] = None
77
+ self.arena: Optional[Arena] = None
78
+ self.insights: List[Insight] = []
79
+ self.hypotheses: List[Hypothesis] = []
80
+ self.moves: List[Move] = []
81
+ self.metrics: List[Metric] = []
82
+
83
+ def to_dict(self) -> Dict[str, Any]:
84
+ """Convert strategy to dictionary"""
85
+ return {
86
+ "goal": asdict(self.goal) if self.goal else None,
87
+ "arena": asdict(self.arena) if self.arena else None,
88
+ "insights": [asdict(i) for i in self.insights],
89
+ "hypotheses": [asdict(h) for h in self.hypotheses],
90
+ "moves": [asdict(m) for m in self.moves],
91
+ "metrics": [asdict(m) for m in self.metrics]
92
+ }
93
+
94
+ def to_json(self, filepath: str):
95
+ """Save strategy to JSON file"""
96
+ with open(filepath, 'w', encoding='utf-8') as f:
97
+ json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
98
+
99
+ @classmethod
100
+ def from_dict(cls, data: Dict[str, Any]) -> 'Strategy':
101
+ """Load strategy from dictionary"""
102
+ strategy = cls()
103
+
104
+ if data.get("goal"):
105
+ strategy.goal = Goal(**data["goal"])
106
+ if data.get("arena"):
107
+ strategy.arena = Arena(**data["arena"])
108
+
109
+ strategy.insights = [Insight(**i) for i in data.get("insights", [])]
110
+ strategy.hypotheses = [Hypothesis(**h) for h in data.get("hypotheses", [])]
111
+ strategy.moves = [Move(**m) for m in data.get("moves", [])]
112
+ strategy.metrics = [Metric(**m) for m in data.get("metrics", [])]
113
+
114
+ return strategy
115
+
116
+ @classmethod
117
+ def from_json(cls, filepath: str) -> 'Strategy':
118
+ """Load strategy from JSON file"""
119
+ with open(filepath, 'r', encoding='utf-8') as f:
120
+ data = json.load(f)
121
+ return cls.from_dict(data)
122
+
123
+
124
+ class SimulationEngine:
125
+ """Strategy simulation and scoring engine"""
126
+
127
+ @staticmethod
128
+ def calculate_move_score(move: Move) -> float:
129
+ """
130
+ Calculate move score using formula:
131
+ score = (impact × fit) × (1 - risk) / cost
132
+ """
133
+ if move.cost == 0:
134
+ return 0
135
+
136
+ score = (move.impact * move.fit) * (1 - move.risk) / (move.cost / 100000)
137
+ return round(score, 4)
138
+
139
+ @staticmethod
140
+ def simulate_strategy(strategy: Strategy) -> Dict[str, Any]:
141
+ """
142
+ Run simulation on complete strategy
143
+ Returns scores, rankings, and forecasts
144
+ """
145
+ results = {
146
+ "move_scores": [],
147
+ "total_impact": 0,
148
+ "metric_forecasts": [],
149
+ "recommendations": []
150
+ }
151
+
152
+ # Calculate scores for each move
153
+ for move in strategy.moves:
154
+ score = SimulationEngine.calculate_move_score(move)
155
+ results["move_scores"].append({
156
+ "id": move.id,
157
+ "text": move.text,
158
+ "score": score,
159
+ "impact": move.impact,
160
+ "fit": move.fit,
161
+ "risk": move.risk,
162
+ "cost": move.cost,
163
+ "linked_hypothesis": move.linked_hypothesis
164
+ })
165
+
166
+ # Sort by score
167
+ results["move_scores"].sort(key=lambda x: x["score"], reverse=True)
168
+
169
+ # Calculate total impact
170
+ total_score = sum(m["score"] for m in results["move_scores"])
171
+ results["total_impact"] = round(total_score, 4)
172
+
173
+ # Forecast main metric (from Goal) first
174
+ if strategy.goal:
175
+ linked_moves = []
176
+ linked_hypotheses = []
177
+ for move in strategy.moves:
178
+ for hyp in strategy.hypotheses:
179
+ if hyp.id == move.linked_hypothesis and hyp.metric == strategy.goal.metric:
180
+ linked_moves.append(move)
181
+ if hyp.id not in linked_hypotheses:
182
+ linked_hypotheses.append(hyp.id)
183
+
184
+ # Calculate forecast and contribution breakdown
185
+ moves_breakdown = []
186
+ for move in linked_moves:
187
+ move_score = SimulationEngine.calculate_move_score(move)
188
+ moves_breakdown.append({
189
+ "id": move.id,
190
+ "text": move.text,
191
+ "score": move_score,
192
+ "hypothesis": move.linked_hypothesis
193
+ })
194
+
195
+ moves_score = sum(m["score"] for m in moves_breakdown)
196
+ forecast = strategy.goal.baseline * (1 + moves_score)
197
+
198
+ results["metric_forecasts"].append({
199
+ "id": strategy.goal.metric,
200
+ "text": f"{strategy.goal.text} (MAIN GOAL)",
201
+ "baseline": strategy.goal.baseline,
202
+ "target": strategy.goal.target,
203
+ "forecast": round(forecast, 2),
204
+ "unit": strategy.goal.unit,
205
+ "gap_to_target": round(strategy.goal.target - forecast, 2),
206
+ "linked_moves": moves_breakdown,
207
+ "linked_hypotheses": linked_hypotheses,
208
+ "is_main": True
209
+ })
210
+
211
+ # Forecast supporting metrics
212
+ for metric in strategy.metrics:
213
+ # Find moves linked to this metric through hypotheses
214
+ linked_moves = []
215
+ linked_hypotheses = []
216
+ for move in strategy.moves:
217
+ for hyp in strategy.hypotheses:
218
+ if hyp.id == move.linked_hypothesis and hyp.metric == metric.id:
219
+ linked_moves.append(move)
220
+ if hyp.id not in linked_hypotheses:
221
+ linked_hypotheses.append(hyp.id)
222
+
223
+ # Calculate forecast and contribution breakdown
224
+ moves_breakdown = []
225
+ for move in linked_moves:
226
+ move_score = SimulationEngine.calculate_move_score(move)
227
+ moves_breakdown.append({
228
+ "id": move.id,
229
+ "text": move.text,
230
+ "score": move_score,
231
+ "hypothesis": move.linked_hypothesis
232
+ })
233
+
234
+ moves_score = sum(m["score"] for m in moves_breakdown)
235
+ forecast = metric.baseline * (1 + moves_score)
236
+
237
+ results["metric_forecasts"].append({
238
+ "id": metric.id,
239
+ "text": metric.text,
240
+ "baseline": metric.baseline,
241
+ "target": metric.target,
242
+ "forecast": round(forecast, 2),
243
+ "unit": metric.unit,
244
+ "gap_to_target": round(metric.target - forecast, 2),
245
+ "linked_moves": moves_breakdown,
246
+ "linked_hypotheses": linked_hypotheses,
247
+ "is_main": False
248
+ })
249
+
250
+ # Generate recommendations
251
+ if results["move_scores"]:
252
+ top_move = results["move_scores"][0]
253
+ if top_move["risk"] > 0.7:
254
+ results["recommendations"].append(f"⚠️ Top move '{top_move['text']}' has high risk ({top_move['risk']})")
255
+
256
+ high_cost_moves = [m for m in results["move_scores"] if m["cost"] > 100000]
257
+ if high_cost_moves:
258
+ results["recommendations"].append(f"💰 {len(high_cost_moves)} move(s) exceed 100k budget")
259
+
260
+ return results
261
+
262
+ @staticmethod
263
+ def create_results_dataframe(results: Dict[str, Any]) -> pd.DataFrame:
264
+ """Convert simulation results to pandas DataFrame"""
265
+ if not results.get("move_scores"):
266
+ return pd.DataFrame()
267
+
268
+ df = pd.DataFrame(results["move_scores"])
269
+ df = df[["id", "text", "score", "impact", "fit", "risk", "cost"]]
270
+ df.columns = ["ID", "Move", "Score", "Impact", "Fit", "Risk", "Cost"]
271
+ return df