tradingcodex 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. apps/__init__.py +1 -0
  2. apps/audit/__init__.py +1 -0
  3. apps/audit/admin.py +6 -0
  4. apps/audit/apps.py +8 -0
  5. apps/audit/migrations/0001_initial.py +35 -0
  6. apps/audit/migrations/__init__.py +0 -0
  7. apps/audit/models.py +22 -0
  8. apps/harness/__init__.py +1 -0
  9. apps/harness/admin.py +6 -0
  10. apps/harness/apps.py +8 -0
  11. apps/harness/migrations/0001_initial.py +35 -0
  12. apps/harness/migrations/__init__.py +0 -0
  13. apps/harness/models.py +22 -0
  14. apps/harness/templatetags/__init__.py +1 -0
  15. apps/integrations/__init__.py +1 -0
  16. apps/integrations/admin.py +6 -0
  17. apps/integrations/apps.py +8 -0
  18. apps/integrations/migrations/0001_initial.py +29 -0
  19. apps/integrations/migrations/__init__.py +0 -0
  20. apps/integrations/models.py +16 -0
  21. apps/integrations/services.py +31 -0
  22. apps/mcp/__init__.py +1 -0
  23. apps/mcp/admin.py +20 -0
  24. apps/mcp/apps.py +8 -0
  25. apps/mcp/migrations/0001_initial.py +168 -0
  26. apps/mcp/migrations/__init__.py +0 -0
  27. apps/mcp/models.py +154 -0
  28. apps/mcp/services.py +327 -0
  29. apps/orders/__init__.py +1 -0
  30. apps/orders/admin.py +6 -0
  31. apps/orders/apps.py +8 -0
  32. apps/orders/migrations/0001_initial.py +79 -0
  33. apps/orders/migrations/__init__.py +0 -0
  34. apps/orders/models.py +66 -0
  35. apps/orders/services.py +107 -0
  36. apps/policy/__init__.py +1 -0
  37. apps/policy/admin.py +6 -0
  38. apps/policy/apps.py +8 -0
  39. apps/policy/migrations/0001_initial.py +75 -0
  40. apps/policy/migrations/__init__.py +0 -0
  41. apps/policy/models.py +61 -0
  42. apps/policy/services.py +110 -0
  43. apps/portfolio/__init__.py +1 -0
  44. apps/portfolio/admin.py +6 -0
  45. apps/portfolio/apps.py +8 -0
  46. apps/portfolio/migrations/0001_initial.py +67 -0
  47. apps/portfolio/migrations/__init__.py +0 -0
  48. apps/portfolio/models.py +53 -0
  49. apps/research/__init__.py +1 -0
  50. apps/research/admin.py +1 -0
  51. apps/research/apps.py +8 -0
  52. apps/research/migrations/__init__.py +0 -0
  53. apps/research/models.py +1 -0
  54. apps/workflows/__init__.py +1 -0
  55. apps/workflows/admin.py +6 -0
  56. apps/workflows/apps.py +8 -0
  57. apps/workflows/migrations/0001_initial.py +51 -0
  58. apps/workflows/migrations/__init__.py +0 -0
  59. apps/workflows/models.py +44 -0
  60. tradingcodex-0.1.0.dist-info/METADATA +337 -0
  61. tradingcodex-0.1.0.dist-info/RECORD +254 -0
  62. tradingcodex-0.1.0.dist-info/WHEEL +5 -0
  63. tradingcodex-0.1.0.dist-info/entry_points.txt +2 -0
  64. tradingcodex-0.1.0.dist-info/licenses/LICENSE +202 -0
  65. tradingcodex-0.1.0.dist-info/licenses/NOTICE +24 -0
  66. tradingcodex-0.1.0.dist-info/top_level.txt +4 -0
  67. tradingcodex_cli/__init__.py +1 -0
  68. tradingcodex_cli/__main__.py +124 -0
  69. tradingcodex_cli/commands/__init__.py +2 -0
  70. tradingcodex_cli/commands/bootstrap.py +157 -0
  71. tradingcodex_cli/commands/db.py +36 -0
  72. tradingcodex_cli/commands/doctor.py +230 -0
  73. tradingcodex_cli/commands/mcp.py +186 -0
  74. tradingcodex_cli/commands/orders.py +89 -0
  75. tradingcodex_cli/commands/policy.py +21 -0
  76. tradingcodex_cli/commands/profile.py +110 -0
  77. tradingcodex_cli/commands/research.py +76 -0
  78. tradingcodex_cli/commands/skills.py +93 -0
  79. tradingcodex_cli/commands/strategies.py +67 -0
  80. tradingcodex_cli/commands/subagents.py +106 -0
  81. tradingcodex_cli/commands/utils.py +134 -0
  82. tradingcodex_cli/commands/workspaces.py +53 -0
  83. tradingcodex_cli/generator.py +234 -0
  84. tradingcodex_cli/mcp_stdio.py +26 -0
  85. tradingcodex_cli/service_autostart.py +127 -0
  86. tradingcodex_service/__init__.py +5 -0
  87. tradingcodex_service/admin.py +1 -0
  88. tradingcodex_service/api.py +486 -0
  89. tradingcodex_service/application/__init__.py +2 -0
  90. tradingcodex_service/application/agents.py +1470 -0
  91. tradingcodex_service/application/audit.py +71 -0
  92. tradingcodex_service/application/common.py +88 -0
  93. tradingcodex_service/application/components.py +299 -0
  94. tradingcodex_service/application/harness.py +747 -0
  95. tradingcodex_service/application/markdown_preview.py +179 -0
  96. tradingcodex_service/application/orders.py +404 -0
  97. tradingcodex_service/application/policy.py +150 -0
  98. tradingcodex_service/application/portfolio.py +166 -0
  99. tradingcodex_service/application/research.py +356 -0
  100. tradingcodex_service/application/runtime.py +321 -0
  101. tradingcodex_service/asgi.py +6 -0
  102. tradingcodex_service/mcp_http.py +59 -0
  103. tradingcodex_service/mcp_runtime.py +565 -0
  104. tradingcodex_service/settings.py +91 -0
  105. tradingcodex_service/templates/web/activity.html +24 -0
  106. tradingcodex_service/templates/web/agent_skills.html +111 -0
  107. tradingcodex_service/templates/web/agents.html +121 -0
  108. tradingcodex_service/templates/web/base.html +150 -0
  109. tradingcodex_service/templates/web/dashboard.html +109 -0
  110. tradingcodex_service/templates/web/fragments/role_inspector.html +81 -0
  111. tradingcodex_service/templates/web/fragments/starter_prompt.html +24 -0
  112. tradingcodex_service/templates/web/fragments/topology_canvas.html +85 -0
  113. tradingcodex_service/templates/web/harness.html +87 -0
  114. tradingcodex_service/templates/web/mcp_router.html +250 -0
  115. tradingcodex_service/templates/web/orders.html +68 -0
  116. tradingcodex_service/templates/web/policy.html +81 -0
  117. tradingcodex_service/templates/web/portfolio.html +52 -0
  118. tradingcodex_service/templates/web/research.html +73 -0
  119. tradingcodex_service/templates/web/starter_prompt.html +40 -0
  120. tradingcodex_service/templates/web/strategies.html +74 -0
  121. tradingcodex_service/urls.py +42 -0
  122. tradingcodex_service/version.py +1 -0
  123. tradingcodex_service/web.py +885 -0
  124. tradingcodex_service/wsgi.py +6 -0
  125. workspace_templates/__init__.py +1 -0
  126. workspace_templates/modules/audit/files/.tradingcodex/audit/README.md +5 -0
  127. workspace_templates/modules/audit/files/trading/audit/.gitkeep +1 -0
  128. workspace_templates/modules/audit/module.json +16 -0
  129. workspace_templates/modules/codex-base/files/.codex/config.toml +272 -0
  130. workspace_templates/modules/codex-base/files/.codex/hooks/tradingcodex_hook.py +173 -0
  131. workspace_templates/modules/codex-base/files/.codex/hooks.json +105 -0
  132. workspace_templates/modules/codex-base/files/.codex/prompts/base_instructions/head-manager.md +129 -0
  133. workspace_templates/modules/codex-base/files/.codex/rules/tradingcodex.rules +50 -0
  134. workspace_templates/modules/codex-base/files/.tradingcodex/capabilities.yaml +56 -0
  135. workspace_templates/modules/codex-base/files/.tradingcodex/cli.py +16 -0
  136. workspace_templates/modules/codex-base/files/.tradingcodex/config.yaml +53 -0
  137. workspace_templates/modules/codex-base/files/.tradingcodex/policies/policy-bindings.yaml +16 -0
  138. workspace_templates/modules/codex-base/files/.tradingcodex/policies/principals.yaml +17 -0
  139. workspace_templates/modules/codex-base/files/.tradingcodex/policies/roles.yaml +27 -0
  140. workspace_templates/modules/codex-base/files/AGENTS.md +56 -0
  141. workspace_templates/modules/codex-base/files/pyproject.toml +13 -0
  142. workspace_templates/modules/codex-base/files/tcx +35 -0
  143. workspace_templates/modules/codex-base/module.json +17 -0
  144. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/policies/access-policies.yaml +39 -0
  145. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/policies/restricted-list.yaml +6 -0
  146. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/approval_receipt.schema.json +14 -0
  147. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/audit_event.schema.json +11 -0
  148. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/evidence_pack.schema.json +16 -0
  149. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/execution_result.schema.json +12 -0
  150. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/fundamental_report.schema.json +15 -0
  151. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/news_report.schema.json +15 -0
  152. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/order_intent.schema.json +30 -0
  153. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/portfolio_review.schema.json +15 -0
  154. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/postmortem_report.schema.json +14 -0
  155. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/risk_report.schema.json +15 -0
  156. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/technical_report.schema.json +15 -0
  157. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/thesis.schema.json +15 -0
  158. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/schemas/valuation.schema.json +15 -0
  159. workspace_templates/modules/enforcement-guardrails/files/.tradingcodex/scripts/validate-order-intent.py +15 -0
  160. workspace_templates/modules/enforcement-guardrails/module.json +19 -0
  161. workspace_templates/modules/fixed-subagents/files/.codex/agents/execution-operator.toml +71 -0
  162. workspace_templates/modules/fixed-subagents/files/.codex/agents/fundamental-analyst.toml +62 -0
  163. workspace_templates/modules/fixed-subagents/files/.codex/agents/instrument-analyst.toml +64 -0
  164. workspace_templates/modules/fixed-subagents/files/.codex/agents/macro-analyst.toml +64 -0
  165. workspace_templates/modules/fixed-subagents/files/.codex/agents/news-analyst.toml +63 -0
  166. workspace_templates/modules/fixed-subagents/files/.codex/agents/portfolio-manager.toml +62 -0
  167. workspace_templates/modules/fixed-subagents/files/.codex/agents/risk-manager.toml +65 -0
  168. workspace_templates/modules/fixed-subagents/files/.codex/agents/technical-analyst.toml +63 -0
  169. workspace_templates/modules/fixed-subagents/files/.codex/agents/valuation-analyst.toml +59 -0
  170. workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/head-manager.yaml +66 -0
  171. workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/skill-change-proposals/.gitkeep +1 -0
  172. workspace_templates/modules/fixed-subagents/files/.tradingcodex/mainagent/subagent-registry.yaml +56 -0
  173. workspace_templates/modules/fixed-subagents/module.json +23 -0
  174. workspace_templates/modules/guidance-guardrails/files/.tradingcodex/guidance/guardrails.md +17 -0
  175. workspace_templates/modules/guidance-guardrails/files/.tradingcodex/guidance/task-quality-checklist.md +37 -0
  176. workspace_templates/modules/guidance-guardrails/module.json +18 -0
  177. workspace_templates/modules/information-barriers/files/.tradingcodex/policies/information-barriers.yaml +211 -0
  178. workspace_templates/modules/information-barriers/files/.tradingcodex/secrets.md +9 -0
  179. workspace_templates/modules/information-barriers/files/trading/approvals/.gitkeep +1 -0
  180. workspace_templates/modules/information-barriers/files/trading/market-data/.gitkeep +1 -0
  181. workspace_templates/modules/information-barriers/files/trading/orders/approved/.gitkeep +1 -0
  182. workspace_templates/modules/information-barriers/files/trading/orders/draft/.gitkeep +1 -0
  183. workspace_templates/modules/information-barriers/files/trading/orders/executed/.gitkeep +1 -0
  184. workspace_templates/modules/information-barriers/files/trading/orders/rejected/.gitkeep +1 -0
  185. workspace_templates/modules/information-barriers/files/trading/portfolio/.gitkeep +1 -0
  186. workspace_templates/modules/information-barriers/files/trading/reports/fundamental/.gitkeep +1 -0
  187. workspace_templates/modules/information-barriers/files/trading/reports/instrument/.gitkeep +1 -0
  188. workspace_templates/modules/information-barriers/files/trading/reports/macro/.gitkeep +1 -0
  189. workspace_templates/modules/information-barriers/files/trading/reports/news/.gitkeep +1 -0
  190. workspace_templates/modules/information-barriers/files/trading/reports/policy/.gitkeep +1 -0
  191. workspace_templates/modules/information-barriers/files/trading/reports/portfolio/.gitkeep +1 -0
  192. workspace_templates/modules/information-barriers/files/trading/reports/postmortem/.gitkeep +1 -0
  193. workspace_templates/modules/information-barriers/files/trading/reports/risk/.gitkeep +1 -0
  194. workspace_templates/modules/information-barriers/files/trading/reports/technical/.gitkeep +1 -0
  195. workspace_templates/modules/information-barriers/files/trading/reports/valuation/.gitkeep +1 -0
  196. workspace_templates/modules/information-barriers/files/trading/research/.gitkeep +1 -0
  197. workspace_templates/modules/information-barriers/module.json +19 -0
  198. workspace_templates/modules/paper-trading/files/.tradingcodex/mcp/adapters/paper-trading.py +4 -0
  199. workspace_templates/modules/paper-trading/module.json +16 -0
  200. workspace_templates/modules/postmortem/files/.tradingcodex/workflows/postmortem.yaml +12 -0
  201. workspace_templates/modules/postmortem/module.json +16 -0
  202. workspace_templates/modules/repo-skills/files/.agents/skills/investment-workflow-map/SKILL.md +106 -0
  203. workspace_templates/modules/repo-skills/files/.agents/skills/investment-workflow-map/agents/openai.yaml +6 -0
  204. workspace_templates/modules/repo-skills/files/.agents/skills/manage-optional-skills/SKILL.md +101 -0
  205. workspace_templates/modules/repo-skills/files/.agents/skills/manage-optional-skills/agents/openai.yaml +6 -0
  206. workspace_templates/modules/repo-skills/files/.agents/skills/manage-subagents/SKILL.md +140 -0
  207. workspace_templates/modules/repo-skills/files/.agents/skills/manage-subagents/agents/openai.yaml +6 -0
  208. workspace_templates/modules/repo-skills/files/.agents/skills/orchestrate-workflow/SKILL.md +140 -0
  209. workspace_templates/modules/repo-skills/files/.agents/skills/orchestrate-workflow/agents/openai.yaml +6 -0
  210. workspace_templates/modules/repo-skills/files/.agents/skills/postmortem/SKILL.md +31 -0
  211. workspace_templates/modules/repo-skills/files/.agents/skills/postmortem/agents/openai.yaml +6 -0
  212. workspace_templates/modules/repo-skills/files/.agents/skills/scenario-quality-gates/SKILL.md +138 -0
  213. workspace_templates/modules/repo-skills/files/.agents/skills/scenario-quality-gates/agents/openai.yaml +6 -0
  214. workspace_templates/modules/repo-skills/files/.agents/skills/strategy-creator/SKILL.md +109 -0
  215. workspace_templates/modules/repo-skills/files/.agents/skills/strategy-creator/agents/openai.yaml +6 -0
  216. workspace_templates/modules/repo-skills/files/.agents/skills/synthesize-decision/SKILL.md +54 -0
  217. workspace_templates/modules/repo-skills/files/.agents/skills/synthesize-decision/agents/openai.yaml +6 -0
  218. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/execution-operator/execute-paper-order/SKILL.md +35 -0
  219. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/execution-operator/execute-paper-order/agents/openai.yaml +6 -0
  220. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/fundamental-analyst/fundamental-analysis/SKILL.md +46 -0
  221. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/fundamental-analyst/fundamental-analysis/agents/openai.yaml +6 -0
  222. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/instrument-analyst/instrument-analysis/SKILL.md +40 -0
  223. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/instrument-analyst/instrument-analysis/agents/openai.yaml +6 -0
  224. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/macro-analyst/macro-analysis/SKILL.md +40 -0
  225. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/macro-analyst/macro-analysis/agents/openai.yaml +6 -0
  226. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/news-analyst/news-analysis/SKILL.md +43 -0
  227. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/news-analyst/news-analysis/agents/openai.yaml +6 -0
  228. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/create-order-intent/SKILL.md +46 -0
  229. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/create-order-intent/agents/openai.yaml +6 -0
  230. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/portfolio-review/SKILL.md +44 -0
  231. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/portfolio-manager/portfolio-review/agents/openai.yaml +6 -0
  232. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/approve-order/SKILL.md +38 -0
  233. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/approve-order/agents/openai.yaml +6 -0
  234. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/policy-review/SKILL.md +43 -0
  235. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/policy-review/agents/openai.yaml +6 -0
  236. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/review-risk/SKILL.md +45 -0
  237. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/risk-manager/review-risk/agents/openai.yaml +6 -0
  238. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/collect-evidence/SKILL.md +46 -0
  239. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/collect-evidence/agents/openai.yaml +6 -0
  240. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/external-data-source-gate/SKILL.md +66 -0
  241. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/shared/external-data-source-gate/agents/openai.yaml +6 -0
  242. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/technical-analyst/technical-analysis/SKILL.md +43 -0
  243. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/technical-analyst/technical-analysis/agents/openai.yaml +6 -0
  244. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/valuation-analyst/valuation-review/SKILL.md +47 -0
  245. workspace_templates/modules/repo-skills/files/.tradingcodex/subagents/skills/valuation-analyst/valuation-review/agents/openai.yaml +6 -0
  246. workspace_templates/modules/repo-skills/module.json +37 -0
  247. workspace_templates/modules/stub-execution/files/.tradingcodex/mcp/adapters/stub-execution.py +4 -0
  248. workspace_templates/modules/stub-execution/module.json +15 -0
  249. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/adapters/live-adapter.contract.md +25 -0
  250. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/enforcer/README.md +5 -0
  251. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/gateway/README.md +8 -0
  252. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/server.py +27 -0
  253. workspace_templates/modules/tradingcodex-mcp/files/.tradingcodex/mcp/smoke-call.py +18 -0
  254. workspace_templates/modules/tradingcodex-mcp/module.json +24 -0
@@ -0,0 +1,75 @@
1
+ # Generated by Django 5.2.5 on 2026-06-12 16:32
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ initial = True
10
+
11
+ dependencies = [
12
+ ]
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='PolicyDecision',
17
+ fields=[
18
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+ ('created_at', models.DateTimeField(auto_now_add=True)),
20
+ ('principal_id', models.CharField(max_length=128)),
21
+ ('action', models.CharField(max_length=160)),
22
+ ('resource', models.CharField(blank=True, max_length=255)),
23
+ ('decision', models.CharField(max_length=16)),
24
+ ('reasons', models.JSONField(blank=True, default=list)),
25
+ ('workspace_context', models.JSONField(blank=True, default=dict)),
26
+ ],
27
+ options={
28
+ 'verbose_name': 'Policy decision',
29
+ 'verbose_name_plural': 'Policy decisions',
30
+ 'ordering': ['-created_at', '-id'],
31
+ },
32
+ ),
33
+ migrations.CreateModel(
34
+ name='Principal',
35
+ fields=[
36
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
37
+ ('principal_id', models.CharField(max_length=128, unique=True)),
38
+ ('role', models.CharField(max_length=128)),
39
+ ('active', models.BooleanField(default=True)),
40
+ ],
41
+ options={
42
+ 'verbose_name': 'Principal',
43
+ 'verbose_name_plural': 'Principals',
44
+ },
45
+ ),
46
+ migrations.CreateModel(
47
+ name='RestrictedSymbol',
48
+ fields=[
49
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
50
+ ('symbol', models.CharField(max_length=64, unique=True)),
51
+ ('reason', models.TextField(blank=True)),
52
+ ('active', models.BooleanField(default=True)),
53
+ ('created_at', models.DateTimeField(auto_now_add=True)),
54
+ ],
55
+ options={
56
+ 'verbose_name': 'Restricted symbol',
57
+ 'verbose_name_plural': 'Restricted symbols',
58
+ },
59
+ ),
60
+ migrations.CreateModel(
61
+ name='Capability',
62
+ fields=[
63
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
64
+ ('action', models.CharField(max_length=160)),
65
+ ('resource_pattern', models.CharField(default='*', max_length=255)),
66
+ ('effect', models.CharField(choices=[('allow', 'Allow'), ('deny', 'Deny')], default='allow', max_length=16)),
67
+ ('principal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='capabilities', to='policy.principal')),
68
+ ],
69
+ options={
70
+ 'verbose_name': 'Capability',
71
+ 'verbose_name_plural': 'Capabilities',
72
+ 'unique_together': {('principal', 'action', 'resource_pattern')},
73
+ },
74
+ ),
75
+ ]
File without changes
apps/policy/models.py ADDED
@@ -0,0 +1,61 @@
1
+ from django.db import models
2
+
3
+
4
+ class Principal(models.Model):
5
+ principal_id = models.CharField(max_length=128, unique=True)
6
+ role = models.CharField(max_length=128)
7
+ active = models.BooleanField(default=True)
8
+
9
+ class Meta:
10
+ verbose_name = "Principal"
11
+ verbose_name_plural = "Principals"
12
+
13
+ def __str__(self) -> str:
14
+ return self.principal_id
15
+
16
+
17
+ class Capability(models.Model):
18
+ principal = models.ForeignKey(Principal, on_delete=models.CASCADE, related_name="capabilities")
19
+ action = models.CharField(max_length=160)
20
+ resource_pattern = models.CharField(max_length=255, default="*")
21
+ effect = models.CharField(max_length=16, choices=[("allow", "Allow"), ("deny", "Deny")], default="allow")
22
+
23
+ class Meta:
24
+ unique_together = [("principal", "action", "resource_pattern")]
25
+ verbose_name = "Capability"
26
+ verbose_name_plural = "Capabilities"
27
+
28
+ def __str__(self) -> str:
29
+ return f"{self.principal_id if hasattr(self, 'principal_id') else self.principal} {self.effect} {self.action}"
30
+
31
+
32
+ class RestrictedSymbol(models.Model):
33
+ symbol = models.CharField(max_length=64, unique=True)
34
+ reason = models.TextField(blank=True)
35
+ active = models.BooleanField(default=True)
36
+ created_at = models.DateTimeField(auto_now_add=True)
37
+
38
+ class Meta:
39
+ verbose_name = "Restricted symbol"
40
+ verbose_name_plural = "Restricted symbols"
41
+
42
+ def __str__(self) -> str:
43
+ return self.symbol
44
+
45
+
46
+ class PolicyDecision(models.Model):
47
+ created_at = models.DateTimeField(auto_now_add=True)
48
+ principal_id = models.CharField(max_length=128)
49
+ action = models.CharField(max_length=160)
50
+ resource = models.CharField(max_length=255, blank=True)
51
+ decision = models.CharField(max_length=16)
52
+ reasons = models.JSONField(default=list, blank=True)
53
+ workspace_context = models.JSONField(default=dict, blank=True)
54
+
55
+ class Meta:
56
+ ordering = ["-created_at", "-id"]
57
+ verbose_name = "Policy decision"
58
+ verbose_name_plural = "Policy decisions"
59
+
60
+ def __str__(self) -> str:
61
+ return f"{self.decision}: {self.principal_id} {self.action}"
@@ -0,0 +1,110 @@
1
+ from __future__ import annotations
2
+
3
+ from fnmatch import fnmatch
4
+ from typing import Any, Iterable
5
+
6
+ from django.db.models import QuerySet
7
+
8
+ from apps.policy.models import Capability, Principal, RestrictedSymbol
9
+
10
+
11
+ BUILTIN_ROLE_IDS = {
12
+ "head-manager",
13
+ "fundamental-analyst",
14
+ "technical-analyst",
15
+ "news-analyst",
16
+ "macro-analyst",
17
+ "instrument-analyst",
18
+ "valuation-analyst",
19
+ "portfolio-manager",
20
+ "risk-manager",
21
+ "execution-operator",
22
+ }
23
+
24
+
25
+ def tool_capability_action(tool_name: str, capability_required: str = "") -> str:
26
+ return capability_required or f"mcp.tradingcodex.{tool_name}"
27
+
28
+
29
+ def sync_builtin_principals_and_capabilities(tool_specs: Iterable[Any] | None = None) -> None:
30
+ if tool_specs is None:
31
+ from tradingcodex_service.mcp_runtime import TOOL_SPECS
32
+
33
+ tool_specs = TOOL_SPECS
34
+
35
+ for role in sorted(BUILTIN_ROLE_IDS):
36
+ Principal.objects.get_or_create(principal_id=role, defaults={"role": role, "active": True})
37
+
38
+ for tool in tool_specs:
39
+ action = tool_capability_action(tool.name, tool.capability_required)
40
+ for role in tool.allowed_roles:
41
+ principal, _ = Principal.objects.get_or_create(principal_id=role, defaults={"role": role, "active": True})
42
+ if principal.role != role:
43
+ principal.role = role
44
+ principal.save(update_fields=["role"])
45
+ Capability.objects.get_or_create(
46
+ principal=principal,
47
+ action=action,
48
+ resource_pattern="*",
49
+ defaults={"effect": "allow"},
50
+ )
51
+
52
+
53
+ def role_for_principal_id(principal_id: str) -> str:
54
+ principal = Principal.objects.filter(principal_id=principal_id).first()
55
+ if principal is not None:
56
+ return principal.role if principal.active else ""
57
+ if principal_id in BUILTIN_ROLE_IDS:
58
+ return principal_id
59
+ return principal_id or "unknown"
60
+
61
+
62
+ def capability_check(principal_id: str, action: str, resource: str | None = None) -> tuple[bool, list[str]]:
63
+ principal = Principal.objects.filter(principal_id=principal_id).first()
64
+ if principal is None:
65
+ return False, [f"principal is unknown: {principal_id}"]
66
+ if not principal.active:
67
+ return False, [f"principal is inactive: {principal_id}"]
68
+
69
+ resource_value = str(resource or "*")
70
+ candidates = [
71
+ capability
72
+ for capability in Capability.objects.filter(principal=principal, action=action)
73
+ if resource_matches(capability.resource_pattern, resource_value)
74
+ ]
75
+ if any(capability.effect == "deny" for capability in candidates):
76
+ return False, [f"capability denied: {principal_id} {action} {resource_value}"]
77
+ if any(capability.effect == "allow" for capability in candidates):
78
+ return True, []
79
+ return False, [f"principal lacks capability: {principal_id} {action} {resource_value}"]
80
+
81
+
82
+ def resource_matches(pattern: str, resource: str) -> bool:
83
+ normalized = pattern or "*"
84
+ return normalized == "*" or fnmatch(resource, normalized)
85
+
86
+
87
+ def set_principal_active(queryset: QuerySet[Principal], active: bool, actor: str = "admin") -> int:
88
+ count = queryset.update(active=active)
89
+ _audit("principal.activated" if active else "principal.deactivated", {"count": count}, actor)
90
+ return count
91
+
92
+
93
+ def set_capability_effect(queryset: QuerySet[Capability], effect: str, actor: str = "admin") -> int:
94
+ if effect not in {"allow", "deny"}:
95
+ raise ValueError("capability effect must be allow or deny")
96
+ count = queryset.update(effect=effect)
97
+ _audit("capability.allowed" if effect == "allow" else "capability.denied", {"count": count}, actor)
98
+ return count
99
+
100
+
101
+ def set_restricted_symbols_active(queryset: QuerySet[RestrictedSymbol], active: bool, actor: str = "admin") -> int:
102
+ count = queryset.update(active=active)
103
+ _audit("restricted_symbol.activated" if active else "restricted_symbol.deactivated", {"count": count}, actor)
104
+ return count
105
+
106
+
107
+ def _audit(action: str, payload: dict[str, Any], actor: str) -> None:
108
+ from tradingcodex_service.application.audit import write_audit_event_if_available
109
+
110
+ write_audit_event_if_available(None, actor, "admin", {"type": action, "payload": payload})
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,6 @@
1
+ from django.contrib import admin
2
+
3
+ from apps.portfolio.models import CashBalance, PortfolioSnapshot, Position
4
+
5
+
6
+ admin.site.register([CashBalance, PortfolioSnapshot, Position])
apps/portfolio/apps.py ADDED
@@ -0,0 +1,8 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class PortfolioConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "apps.portfolio"
7
+ label = "portfolio"
8
+ verbose_name = "Portfolio State"
@@ -0,0 +1,67 @@
1
+ # Generated by Django 5.2.5 on 2026-06-12 16:32
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ initial = True
10
+
11
+ dependencies = [
12
+ ]
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='PortfolioSnapshot',
17
+ fields=[
18
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+ ('created_at', models.DateTimeField(auto_now_add=True)),
20
+ ('source', models.CharField(default='paper-trading', max_length=64)),
21
+ ('portfolio_id', models.CharField(default='default-paper', max_length=120)),
22
+ ('account_id', models.CharField(default='local-paper', max_length=120)),
23
+ ('strategy_id', models.CharField(default='default-strategy', max_length=120)),
24
+ ('workspace_context', models.JSONField(blank=True, default=dict)),
25
+ ('payload', models.JSONField(blank=True, default=dict)),
26
+ ],
27
+ options={
28
+ 'verbose_name': 'Portfolio snapshot',
29
+ 'verbose_name_plural': 'Portfolio snapshots',
30
+ 'ordering': ['-created_at', '-id'],
31
+ },
32
+ ),
33
+ migrations.CreateModel(
34
+ name='CashBalance',
35
+ fields=[
36
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
37
+ ('currency', models.CharField(default='KRW', max_length=16)),
38
+ ('amount', models.DecimalField(decimal_places=2, max_digits=24)),
39
+ ('portfolio_id', models.CharField(default='default-paper', max_length=120)),
40
+ ('account_id', models.CharField(default='local-paper', max_length=120)),
41
+ ('strategy_id', models.CharField(default='default-strategy', max_length=120)),
42
+ ('snapshot', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cash_balances', to='portfolio.portfoliosnapshot')),
43
+ ],
44
+ options={
45
+ 'verbose_name': 'Cash balance',
46
+ 'verbose_name_plural': 'Cash balances',
47
+ },
48
+ ),
49
+ migrations.CreateModel(
50
+ name='Position',
51
+ fields=[
52
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
53
+ ('symbol', models.CharField(max_length=64)),
54
+ ('quantity', models.DecimalField(decimal_places=6, max_digits=20)),
55
+ ('average_price', models.DecimalField(decimal_places=6, max_digits=20)),
56
+ ('currency', models.CharField(default='KRW', max_length=16)),
57
+ ('portfolio_id', models.CharField(default='default-paper', max_length=120)),
58
+ ('account_id', models.CharField(default='local-paper', max_length=120)),
59
+ ('strategy_id', models.CharField(default='default-strategy', max_length=120)),
60
+ ('snapshot', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='positions', to='portfolio.portfoliosnapshot')),
61
+ ],
62
+ options={
63
+ 'verbose_name': 'Position',
64
+ 'verbose_name_plural': 'Positions',
65
+ },
66
+ ),
67
+ ]
File without changes
@@ -0,0 +1,53 @@
1
+ from django.db import models
2
+
3
+
4
+ class PortfolioSnapshot(models.Model):
5
+ created_at = models.DateTimeField(auto_now_add=True)
6
+ source = models.CharField(max_length=64, default="paper-trading")
7
+ portfolio_id = models.CharField(max_length=120, default="default-paper")
8
+ account_id = models.CharField(max_length=120, default="local-paper")
9
+ strategy_id = models.CharField(max_length=120, default="default-strategy")
10
+ workspace_context = models.JSONField(default=dict, blank=True)
11
+ payload = models.JSONField(default=dict, blank=True)
12
+
13
+ class Meta:
14
+ ordering = ["-created_at", "-id"]
15
+ verbose_name = "Portfolio snapshot"
16
+ verbose_name_plural = "Portfolio snapshots"
17
+
18
+ def __str__(self) -> str:
19
+ return f"{self.source} {self.created_at:%Y-%m-%d %H:%M:%S}"
20
+
21
+
22
+ class Position(models.Model):
23
+ snapshot = models.ForeignKey(PortfolioSnapshot, on_delete=models.CASCADE, related_name="positions")
24
+ symbol = models.CharField(max_length=64)
25
+ quantity = models.DecimalField(max_digits=20, decimal_places=6)
26
+ average_price = models.DecimalField(max_digits=20, decimal_places=6)
27
+ currency = models.CharField(max_length=16, default="KRW")
28
+ portfolio_id = models.CharField(max_length=120, default="default-paper")
29
+ account_id = models.CharField(max_length=120, default="local-paper")
30
+ strategy_id = models.CharField(max_length=120, default="default-strategy")
31
+
32
+ class Meta:
33
+ verbose_name = "Position"
34
+ verbose_name_plural = "Positions"
35
+
36
+ def __str__(self) -> str:
37
+ return f"{self.symbol} {self.quantity}"
38
+
39
+
40
+ class CashBalance(models.Model):
41
+ snapshot = models.ForeignKey(PortfolioSnapshot, on_delete=models.CASCADE, related_name="cash_balances")
42
+ currency = models.CharField(max_length=16, default="KRW")
43
+ amount = models.DecimalField(max_digits=24, decimal_places=2)
44
+ portfolio_id = models.CharField(max_length=120, default="default-paper")
45
+ account_id = models.CharField(max_length=120, default="local-paper")
46
+ strategy_id = models.CharField(max_length=120, default="default-strategy")
47
+
48
+ class Meta:
49
+ verbose_name = "Cash balance"
50
+ verbose_name_plural = "Cash balances"
51
+
52
+ def __str__(self) -> str:
53
+ return f"{self.currency} {self.amount}"
@@ -0,0 +1 @@
1
+
apps/research/admin.py ADDED
@@ -0,0 +1 @@
1
+ """Research artifacts are workspace files, not Django Admin models."""
apps/research/apps.py ADDED
@@ -0,0 +1,8 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class ResearchConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "apps.research"
7
+ label = "research"
8
+ verbose_name = "Research Memory"
File without changes
@@ -0,0 +1 @@
1
+ """Research is file-native; this Django app intentionally owns no DB models."""
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,6 @@
1
+ from django.contrib import admin
2
+
3
+ from apps.workflows.models import ArtifactRef, WorkflowRun
4
+
5
+
6
+ admin.site.register([ArtifactRef, WorkflowRun])
apps/workflows/apps.py ADDED
@@ -0,0 +1,8 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class WorkflowsConfig(AppConfig):
5
+ default_auto_field = "django.db.models.BigAutoField"
6
+ name = "apps.workflows"
7
+ label = "workflows"
8
+ verbose_name = "Workflow Runs"
@@ -0,0 +1,51 @@
1
+ # Generated by Django 5.2.5 on 2026-06-12 16:32
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ initial = True
10
+
11
+ dependencies = [
12
+ ]
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='WorkflowRun',
17
+ fields=[
18
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+ ('run_id', models.CharField(max_length=180, unique=True)),
20
+ ('lane', models.CharField(max_length=80)),
21
+ ('universe', models.CharField(default='public_equity', max_length=80)),
22
+ ('readiness_label', models.CharField(default='factual-baseline', max_length=80)),
23
+ ('status', models.CharField(default='open', max_length=32)),
24
+ ('original_request', models.TextField(blank=True)),
25
+ ('workspace_context', models.JSONField(blank=True, default=dict)),
26
+ ('created_at', models.DateTimeField(auto_now_add=True)),
27
+ ('updated_at', models.DateTimeField(auto_now=True)),
28
+ ],
29
+ options={
30
+ 'verbose_name': 'Workflow run',
31
+ 'verbose_name_plural': 'Workflow runs',
32
+ },
33
+ ),
34
+ migrations.CreateModel(
35
+ name='ArtifactRef',
36
+ fields=[
37
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
38
+ ('path', models.CharField(max_length=512)),
39
+ ('artifact_type', models.CharField(max_length=80)),
40
+ ('role', models.CharField(blank=True, max_length=128)),
41
+ ('handoff_state', models.CharField(choices=[('accepted', 'accepted'), ('revise', 'revise'), ('blocked', 'blocked'), ('waiting', 'waiting')], default='waiting', max_length=32)),
42
+ ('hero', models.BooleanField(default=False)),
43
+ ('created_at', models.DateTimeField(auto_now_add=True)),
44
+ ('workflow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='artifacts', to='workflows.workflowrun')),
45
+ ],
46
+ options={
47
+ 'verbose_name': 'Artifact reference',
48
+ 'verbose_name_plural': 'Artifact references',
49
+ },
50
+ ),
51
+ ]
File without changes
@@ -0,0 +1,44 @@
1
+ from django.db import models
2
+
3
+ HANDOFF_STATE_CHOICES = [
4
+ ("accepted", "accepted"),
5
+ ("revise", "revise"),
6
+ ("blocked", "blocked"),
7
+ ("waiting", "waiting"),
8
+ ]
9
+
10
+
11
+ class WorkflowRun(models.Model):
12
+ run_id = models.CharField(max_length=180, unique=True)
13
+ lane = models.CharField(max_length=80)
14
+ universe = models.CharField(max_length=80, default="public_equity")
15
+ readiness_label = models.CharField(max_length=80, default="factual-baseline")
16
+ status = models.CharField(max_length=32, default="open")
17
+ original_request = models.TextField(blank=True)
18
+ workspace_context = models.JSONField(default=dict, blank=True)
19
+ created_at = models.DateTimeField(auto_now_add=True)
20
+ updated_at = models.DateTimeField(auto_now=True)
21
+
22
+ class Meta:
23
+ verbose_name = "Workflow run"
24
+ verbose_name_plural = "Workflow runs"
25
+
26
+ def __str__(self) -> str:
27
+ return self.run_id
28
+
29
+
30
+ class ArtifactRef(models.Model):
31
+ workflow = models.ForeignKey(WorkflowRun, on_delete=models.CASCADE, related_name="artifacts")
32
+ path = models.CharField(max_length=512)
33
+ artifact_type = models.CharField(max_length=80)
34
+ role = models.CharField(max_length=128, blank=True)
35
+ handoff_state = models.CharField(max_length=32, choices=HANDOFF_STATE_CHOICES, default="waiting")
36
+ hero = models.BooleanField(default=False)
37
+ created_at = models.DateTimeField(auto_now_add=True)
38
+
39
+ class Meta:
40
+ verbose_name = "Artifact reference"
41
+ verbose_name_plural = "Artifact references"
42
+
43
+ def __str__(self) -> str:
44
+ return self.path