programgarden 2.5.5__tar.gz → 2.5.6__tar.gz
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.
- {programgarden-2.5.5 → programgarden-2.5.6}/PKG-INFO +1 -1
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/order_recovery.py +29 -8
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/workflow_position_tracker.py +11 -1
- {programgarden-2.5.5 → programgarden-2.5.6}/pyproject.toml +1 -1
- {programgarden-2.5.5 → programgarden-2.5.6}/README.md +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/__init__.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/binding_validator.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/client.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/code_worker.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/context.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/__init__.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/adjustment_delivery.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/broker_evidence.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/broker_order_totals.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/broker_snapshot.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/checkpoint_manager.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/db_naming.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/execution_storage.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/fill_reconciler.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/order_cancellation.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/owned_order_cancellation.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/position_reconciliation.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/query_builder.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/workflow_risk_tracker.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/deep_fixtures.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/executor.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/futures_orderable.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/futures_pnl.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/futures_read_evidence.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/incremental_build.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/managed_order_control.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/node_runner.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/order_lifecycle.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/plugin/__init__.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/plugin/sandbox.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/providers/__init__.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/providers/llm_errors.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/providers/llm_provider.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/reconnect_handler.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/replay_contracts.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/replay_events.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/replay_external.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/replay_order_adapter.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/replay_orders.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/replay_scenarios.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/replay_semantics.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/replay_sources.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/replay_sqlite.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/replay_triggers.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/resolver.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/resource/__init__.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/resource/context.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/resource/limiter.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/resource/monitor.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/resource/throttle.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/semantic_rules.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/tools/__init__.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/tools/credential_tools.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/tools/definition_tools.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/tools/event_tools.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/tools/job_tools.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/tools/registry_tools.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/tools/sqlite_tools.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/validation_recommender.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/validation_replay.py +0 -0
- {programgarden-2.5.5 → programgarden-2.5.6}/programgarden/valuation.py +0 -0
|
@@ -97,6 +97,9 @@ def initialize_recovery(conn):
|
|
|
97
97
|
columns = {row[1] for row in conn.execute("PRAGMA table_info(workflow_position_lots)")}
|
|
98
98
|
if "recovery_id" not in columns:
|
|
99
99
|
conn.execute("ALTER TABLE workflow_position_lots ADD COLUMN recovery_id INTEGER")
|
|
100
|
+
recovery_columns = {row[1] for row in conn.execute("PRAGMA table_info(workflow_order_recoveries)")}
|
|
101
|
+
if "unpriced_quantity" not in recovery_columns:
|
|
102
|
+
conn.execute("ALTER TABLE workflow_order_recoveries ADD COLUMN unpriced_quantity TEXT NOT NULL DEFAULT '0'")
|
|
100
103
|
|
|
101
104
|
|
|
102
105
|
def recovery_for_order(conn, tracker, day, number):
|
|
@@ -159,7 +162,8 @@ def recover_totals(tracker, totals):
|
|
|
159
162
|
with sqlite3.connect(tracker.db_path) as conn:
|
|
160
163
|
conn.execute("BEGIN IMMEDIATE")
|
|
161
164
|
# Aggregate execution times are only a recovery ordering, never invented
|
|
162
|
-
# timestamps for individual executions. Missing basis
|
|
165
|
+
# timestamps for individual executions. Missing basis invalidates PnL,
|
|
166
|
+
# not a broker-verified execution or the later fresh holdings check.
|
|
163
167
|
for total, (number, symbol, total_qty, price) in sorted(
|
|
164
168
|
validated, key=lambda item: (item[0].order_date, item[0].fill_time, int(item[1][0]))):
|
|
165
169
|
scope = tracker.product, tracker.provider, tracker.trading_mode
|
|
@@ -232,13 +236,18 @@ def recover_totals(tracker, totals):
|
|
|
232
236
|
SELECT 1 FROM trade_history WHERE product=? AND provider=? AND trading_mode=?
|
|
233
237
|
AND symbol=? AND classification='workflow' AND side='sell' AND fill_datetime>=? LIMIT 1
|
|
234
238
|
""", (*scope, symbol, fill_datetime)).fetchone()
|
|
239
|
+
later_recovery = conn.execute("""
|
|
240
|
+
SELECT 1 FROM workflow_order_recoveries WHERE product=? AND provider=? AND trading_mode=?
|
|
241
|
+
AND symbol=? AND side='sell' AND fill_datetime>=? LIMIT 1
|
|
242
|
+
""", (*scope, symbol, fill_datetime)).fetchone()
|
|
235
243
|
adjusted = conn.execute("SELECT 1 FROM sqlite_master WHERE name='position_adjustments'").fetchone()
|
|
236
244
|
if adjusted:
|
|
237
245
|
adjusted = conn.execute("""SELECT 1 FROM position_adjustments WHERE product=?
|
|
238
246
|
AND provider=? AND trading_mode=? AND symbol=? LIMIT 1""", (*scope, symbol)).fetchone()
|
|
239
|
-
if later_sell or adjusted:
|
|
247
|
+
if later_sell or later_recovery or adjusted:
|
|
240
248
|
raise ReconciliationUnavailable("historical_buy_requires_full_replay")
|
|
241
249
|
pnl = Decimal(0)
|
|
250
|
+
unpriced = Decimal(0)
|
|
242
251
|
lots = []
|
|
243
252
|
if total.side == "sell" and missing:
|
|
244
253
|
lots = conn.execute("""
|
|
@@ -246,9 +255,12 @@ def recover_totals(tracker, totals):
|
|
|
246
255
|
AND provider=? AND trading_mode=? AND symbol=? AND classification='workflow'
|
|
247
256
|
AND remaining_qty>0 AND fill_datetime<=? ORDER BY fill_datetime,id
|
|
248
257
|
""", (*scope, symbol, fill_datetime)).fetchall()
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
258
|
+
# Account-wide strategies can sell holdings acquired before this
|
|
259
|
+
# workflow. Never invent their purchase lots or a zero-cost basis.
|
|
260
|
+
# Validate stored numbers even when there are too few owned lots.
|
|
261
|
+
for _, basis, available in lots:
|
|
262
|
+
quantity(basis)
|
|
263
|
+
quantity(available)
|
|
252
264
|
cursor = conn.execute("""
|
|
253
265
|
INSERT INTO workflow_order_recoveries
|
|
254
266
|
(product,provider,trading_mode,order_date,order_no,symbol,exchange,side,
|
|
@@ -273,16 +285,25 @@ def recover_totals(tracker, totals):
|
|
|
273
285
|
remainder = missing
|
|
274
286
|
for lot_id, basis, available in lots:
|
|
275
287
|
used = min(remainder, quantity(available))
|
|
276
|
-
|
|
288
|
+
if quantity(basis) > 0:
|
|
289
|
+
pnl += (recovered_price - quantity(basis)) * used
|
|
290
|
+
else:
|
|
291
|
+
unpriced += used
|
|
277
292
|
conn.execute("UPDATE workflow_position_lots SET remaining_qty=? WHERE id=?",
|
|
278
293
|
(float(quantity(available) - used), lot_id))
|
|
279
294
|
remainder -= used
|
|
280
295
|
if not remainder:
|
|
281
296
|
break
|
|
282
|
-
|
|
297
|
+
unpriced += remainder
|
|
298
|
+
# estimated_pnl stores only the matched portion for audit. Public
|
|
299
|
+
# readers must return null whenever any quantity lacks a basis.
|
|
300
|
+
conn.execute("UPDATE workflow_order_recoveries SET estimated_pnl=?, unpriced_quantity=? WHERE id=?",
|
|
301
|
+
(str(pnl), str(unpriced), recovery_id))
|
|
283
302
|
results.append({"id": recovery_id, "order_date": total.order_date, "order_no": number,
|
|
284
303
|
"symbol": symbol, "side": total.side, "quantity": str(missing),
|
|
285
|
-
"estimated_pnl": str(pnl), "currency": total.currency,
|
|
304
|
+
"estimated_pnl": None if unpriced else str(pnl), "currency": total.currency,
|
|
305
|
+
"unpriced_quantity": str(unpriced),
|
|
306
|
+
"cost_basis_status": "unavailable" if unpriced else "estimated",
|
|
286
307
|
"basis": "broker_order_total", "is_estimated": True, "is_trade": False})
|
|
287
308
|
return results
|
|
288
309
|
|
{programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/workflow_position_tracker.py
RENAMED
|
@@ -1818,10 +1818,16 @@ class WorkflowPositionTracker:
|
|
|
1818
1818
|
"""Read the immutable recovery audit; this is not individual fill history."""
|
|
1819
1819
|
with sqlite3.connect(self.db_path) as conn:
|
|
1820
1820
|
conn.row_factory = sqlite3.Row
|
|
1821
|
-
|
|
1821
|
+
rows = [dict(row) for row in conn.execute(
|
|
1822
1822
|
"SELECT * FROM workflow_order_recoveries WHERE product=? AND provider=? "
|
|
1823
1823
|
"AND trading_mode=? ORDER BY id",
|
|
1824
1824
|
(self.product, self.provider, self.trading_mode))]
|
|
1825
|
+
for row in rows:
|
|
1826
|
+
unknown = Decimal(row["unpriced_quantity"]) > 0
|
|
1827
|
+
row["cost_basis_status"] = "unavailable" if unknown else "estimated"
|
|
1828
|
+
if unknown:
|
|
1829
|
+
row["estimated_pnl"] = None
|
|
1830
|
+
return rows
|
|
1825
1831
|
|
|
1826
1832
|
def get_position_adjustments(self, limit: int = 50) -> List[Dict[str, Any]]:
|
|
1827
1833
|
"""Return the current product/mode audit without creating synthetic trades."""
|
|
@@ -2249,6 +2255,10 @@ class WorkflowPositionTracker:
|
|
|
2249
2255
|
reason = "futures_fifo_not_monetary"
|
|
2250
2256
|
elif not symbol:
|
|
2251
2257
|
reason = "missing_symbol"
|
|
2258
|
+
elif any(fill.get("cost_basis_status") == "unavailable" for fill in fills):
|
|
2259
|
+
# Verified order quantity can permit startup without establishing
|
|
2260
|
+
# historical profit. Preserve the existing unavailable contract.
|
|
2261
|
+
reason = "incomplete_fifo_basis"
|
|
2252
2262
|
elif len(units - {None}) > 1 or unit_conflict:
|
|
2253
2263
|
# Contradictory units cannot establish a comparable FIFO basis.
|
|
2254
2264
|
reason = "incomplete_fifo_basis"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/owned_order_cancellation.py
RENAMED
|
File without changes
|
{programgarden-2.5.5 → programgarden-2.5.6}/programgarden/database/position_reconciliation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|