Bug description
Any strategy using MockConnectorConfig (mock/paper trading) crashes update_interval seconds after start:
AttributeError: 'AsyncCache' object has no attribute '_sync_pnl'
The same error is raised a second time during shutdown: engine.dispose() → MockLinearConnector.disconnect() also calls self._cache._sync_pnl(...), so _dispose() aborts before cache.close() runs and the final orders/positions/balances sync to the storage backend is silently skipped.
Steps to reproduce
- Run the bundled example:
python strategy/binance/mock_trading.py (it configures MockConnectorConfig with update_interval=20).
- Wait ~20 seconds. The periodic PnL task dies with the
AttributeError above.
- Stop the engine (Ctrl-C):
engine.dispose() logs Dispose error: 'AsyncCache' object has no attribute '_sync_pnl' and skips the final cache sync.
Root cause
The storage-backend refactor (commit 070609b, "Add PostgreSQL support and update dependencies", issue #27) moved AsyncCache's direct SQLite access into pluggable StorageBackend classes and dropped AsyncCache._sync_pnl() in the process. But:
MockLinearConnector still calls it in two places: _handle_pnl_update() (nexustrader/base/connector.py#L823) and disconnect() (#L832)
- Both
SQLiteBackend and PostgreSQLBackend still create the {table_prefix}_pnl table — only the write path was lost, so the table stays empty forever.
Suggested fix
Restore the PnL write path through the backend abstraction (keeping the MockLinearConnector call sites unchanged):
StorageBackend: add sync_pnl(timestamp, pnl, unrealized_pnl) to the abstract interface
SQLiteBackend / PostgreSQLBackend: upsert the snapshot into the existing {table_prefix}_pnl table (INSERT OR REPLACE / ON CONFLICT (timestamp) DO UPDATE)
AsyncCache: restore _sync_pnl() delegating to self._backend.sync_pnl(...)
I have this implemented with regression tests and can open a PR.
Bug description
Any strategy using
MockConnectorConfig(mock/paper trading) crashesupdate_intervalseconds after start:The same error is raised a second time during shutdown:
engine.dispose()→MockLinearConnector.disconnect()also callsself._cache._sync_pnl(...), so_dispose()aborts beforecache.close()runs and the final orders/positions/balances sync to the storage backend is silently skipped.Steps to reproduce
python strategy/binance/mock_trading.py(it configuresMockConnectorConfigwithupdate_interval=20).AttributeErrorabove.engine.dispose()logsDispose error: 'AsyncCache' object has no attribute '_sync_pnl'and skips the final cache sync.Root cause
The storage-backend refactor (commit
070609b, "Add PostgreSQL support and update dependencies", issue #27) moved AsyncCache's direct SQLite access into pluggableStorageBackendclasses and droppedAsyncCache._sync_pnl()in the process. But:MockLinearConnectorstill calls it in two places:_handle_pnl_update()(nexustrader/base/connector.py#L823) anddisconnect()(#L832)SQLiteBackendandPostgreSQLBackendstill create the{table_prefix}_pnltable — only the write path was lost, so the table stays empty forever.Suggested fix
Restore the PnL write path through the backend abstraction (keeping the
MockLinearConnectorcall sites unchanged):StorageBackend: addsync_pnl(timestamp, pnl, unrealized_pnl)to the abstract interfaceSQLiteBackend/PostgreSQLBackend: upsert the snapshot into the existing{table_prefix}_pnltable (INSERT OR REPLACE/ON CONFLICT (timestamp) DO UPDATE)AsyncCache: restore_sync_pnl()delegating toself._backend.sync_pnl(...)I have this implemented with regression tests and can open a PR.