Copy trading regression scenario¶
copytrading-regression is a standalone destructive scenario in the test stand. It creates isolated master, follower, payout, and supporting accounts; clones the configured deterministic USD Forex symbol into a scenario-specific symbol; drives quotes; sends manager and customer requests; and checks resulting orders and account finances against an independent fixture ledger. Each case gets its own fixtures and cleanup. The scenario restores the selected base snapshot before it starts, so run it only against the dedicated test server.
The scenario requires the configured basic trading group (or TEST_BASIC_USD) and symbol. The group and source symbol must be USD denominated; the source must use Forex profit and margin modes, USD margin and quote currencies, and a 100,000 contract size. The stand needs working manager TCP access, customer HTTP access for the configured test accounts, event capture, controllable quotes, and server restart support. The current contract assumes one deterministic USD Forex fixture; it does not test currency conversion.
Coverage¶
The scenario verifies create, update, list, and delete behavior for masters, followers, subscriptions, and reward policies. It reads complete typed records, checks defaults, accepted values, invalid types and bounds, preservation of untouched fields, timestamp movement, identifiers and foreign keys. Filter checks exercise exact counts, ordering, pagination, comparison and set filters, and malformed filters. The configured fields are:
| Record | Configurable fields exercised |
|---|---|
| Master | enabled, strategy_name (up to 128 characters), description (up to 2,000), visibility (private or public), allowed_follower_modes (0 real, 1 demo, 2 both), show_trade_history, show_followers. The fixture also verifies its login. |
| Follower | enabled, manual_trading_mode (0 disabled, 1 allow non-copied trades, 2 allow all), max_master_count, and login. |
| Subscription | master_login, follower_login, enabled, status (0 active, 1 paused, 2 disabled), copy_mode (0 full sync, 1 open only, 2 close only), volume_mode (0 fixed, 1 multiplier, 2 equity ratio, 3 balance ratio), risk_multiplier, fixed_volume, min_volume, max_volume, max_positions, copy_sl, copy_tp, copy_partial_close, copy_pending_orders, copy_existing_positions, reverse_direction, symbols_mode (0 all, 1 include, 2 exclude), and symbols. |
| Reward policy | master_id, enabled, reward_type (0 subscription fee, 1 performance fee with HWM, 2 volume rebate, 3 profit share per trade, 4 management fee), billing_period, fee_amount, fee_percent, currency, payout_account_login, and hwm_enabled. |
The scenario then opens BUY and SELL market trades and checks fixed and multiplier volumes, plus equity-ratio and balance-ratio calculations with credit separated from balance and zero-master-balance cases. It checks rounding, min/max clamps, position limits, partial closes, all BUY/SELL reverse-direction and SL/TP flag combinations, copy modes, disabled and paused subscriptions, existing-position replay, and symbol include/exclude rules. It also exercises all four pending order command types with pending-copying enabled and disabled, modification of master SL/TP, and master-driven activation at nonzero spread. The activation checks preserve the follower ticket and link, prevent premature activation when reverse-mapped command trigger thresholds (including BUY_LIMIT/SELL_STOP and BUY_STOP/SELL_LIMIT) differ, and verify that paused or disabled subscriptions do not activate followers. The suite also checks rapid bursts, fan-out to multiple followers, insufficient follower funds, recursion guards, and deletion of records while copied positions remain open.
When a copy is rejected, the fixture accepts an optional FAILED link (state: 2) as diagnostic evidence and checks the associated rejected-open trade event (state: 20). It also verifies that no follower position or financial effect appears and that the rejected order does not execute later; the failed link is not treated as a successful copy.
Customer API checks cover ownership, private/public visibility, follower account REAL/DEMO eligibility, CRUD, and attempts to access or mutate another customer's records. Manager checks cover see_copytrade, entity-specific write/delete rights, and group scope for masters, followers, subscriptions, reward policies, links, and accruals. A separate foreign-brand access check uses a flat manager-only account; it does not authenticate that account as a customer or register the synthetic brand, and checks that foreign-brand master reads and updates remain excluded. Finance checks independently compare balances, equity, margin, realized and floating P/L, copied volumes, fee accruals, charge/payout orders, and duplicate settlement behavior. Positive paid rewards are checked across a server restart to ensure they are not charged again; an insufficient-funds case checks the failed accrual, missing charge/payout tickets, failure reason, and unchanged finance. Authenticated customers with and without access to the records are checked against subscription, trade-link, and reward lists.
Statistics are checked for 7D, 30D, 90D, and 365D, including dated net P/L, trade and win counts, daily cumulative history, per-symbol results, marketplace values, privacy flags, and persistence after a server restart. Daily Sharp and Sortino ratios are compared with values computed independently from the dated history. A separate restart case compares complete master, follower, subscription, reward, link and accrual records plus open trades before and after restart, then verifies that copying continues and does not replay existing orders.
Known gaps reported by the scenario¶
A run is not a passing certification merely because configuration records round-trip. The regression cases assert manual-mode enforcement on market and pending trade writes, protected copied-order changes and group requests; active distinct-master limits; pending copy and master-driven activation at nonzero spread with ticket preservation and no premature reverse-mapped trigger; and FULL_SYNC SL/TP synchronization. The burst test checks max_positions and unique links. These behaviors are implemented in the current source; remote runs remain necessary to verify the deployed server.
The supplied remote run dd98fb83 on server build 1.25.269 visited all 97 cases but failed overall, with 600 failed assertions out of 13,422. That build returned an incomplete manager subscription-list row. The current scenario now contains 113 cases, and current source returns all 22 subscription columns; remote rerun is pending to verify the fix. Fixture corrections are prepared. Manager authorization, paid-reward restart, and recursion coverage were blocked in the supplied run. This historical run remains uncertified.
Only PROFIT_SHARE_PER_TRADE is exercised as a working reward settlement path. The other reward types are included in parameter validation and persistence coverage, but are listed as runtime unsupported by the scenario. The current scenario defers nonzero historical equity/drawdown fixtures, a crash between fee charge and payout, cross-currency copy/reward conversion, and copy-specific Stop Out stress. The public trade reads used by this scenario do not expose trade.api_data; fee correlations therefore use order identifiers and comments.
Deferred scope is also listed in the copytrading-coverage checkpoint. A failed check can leave a failed assertion even when its section proceeds to cleanup, and any failed assertion makes the overall scenario fail. Review the failed assertions and the limitations in the coverage checkpoint before treating a run as evidence of support.
Report evidence¶
Assertions are grouped under the Copy trading report category. Use the existing assertion filter to inspect failures:
GET /api/runs/:id/assertions?limit=50&after=0&failedOnly=true§ion=Copy%20trading
copytrading-coverage records parameter names by record type, the runtime-unsupported reward types, deferred areas, unavailable wire fields, and case names. copytrading-summary records each case's status, elapsed time, failed check count and any error or cleanup error, alongside the coverage and whether it is safe to continue to restart cases. A failed section does not stop later independent sections; restart sections are skipped if earlier fixture cleanup could not confirm safe exposure.
Example checkpoint shapes:
{
"parameters": {
"master": ["enabled", "strategy_name", "description", "visibility", "allowed_follower_modes", "show_trade_history", "show_followers"],
"follower": ["enabled", "manual_trading_mode", "max_master_count"],
"subscription": ["enabled", "status", "copy_mode", "volume_mode", "risk_multiplier", "fixed_volume", "copy_sl", "copy_tp", "copy_partial_close", "copy_pending_orders", "copy_existing_positions", "reverse_direction", "min_volume", "max_volume", "max_positions", "symbols_mode", "symbols"],
"reward": ["enabled", "reward_type", "billing_period", "fee_amount", "fee_percent", "currency", "payout_account_login", "hwm_enabled"]
},
"runtimeUnsupported": ["SUBSCRIPTION_FEE", "PERFORMANCE_FEE_HWM", "VOLUME_REBATE", "MANAGEMENT_FEE"],
"deferred": ["nonzero historical equity/drawdown fixtures", "reward crash between charge and payout", "cross-currency copy/reward conversion", "copy-specific Stop Out stress"],
"unavailableWireFields": ["trade.api_data is not exposed by public trade reads; correlation uses order ids and comments"],
"cases": ["contract-master", "filters-master", "volume-0-side-0", "statistics-restart"]
}
A summary checkpoint has this shape (the live run contains all case rows and parameter/case names):
{
"sections": [
{ "section": "contract-master", "status": "passed", "failedChecks": 0, "durationMs": 1200 },
{ "section": "pending-2-1", "status": "failed", "error": "copy_pending_orders did not copy the pending order", "failedChecks": 1, "durationMs": 900 }
],
"coverage": {
"parameters": { "master": ["enabled"] },
"runtimeUnsupported": ["SUBSCRIPTION_FEE", "PERFORMANCE_FEE_HWM", "VOLUME_REBATE", "MANAGEMENT_FEE"],
"deferred": ["nonzero historical equity/drawdown fixtures", "reward crash between charge and payout", "cross-currency copy/reward conversion", "copy-specific Stop Out stress"],
"unavailableWireFields": ["trade.api_data is not exposed by public trade reads; correlation uses order ids and comments"],
"cases": ["contract-master", "pending-2-1"]
},
"safeToRestart": true
}
The sample failure text is illustrative; use the run's stored assertions and checkpoint for the actual result. Run-level status is failed if any case or assertion failed.
Run the scenario¶
The scenario is destructive and estimated at 30 minutes. Run it on the remote test stand against a selected clean snapshot after deploying the matching server and stand builds. First run its non-compiling helper test remotely from test-stand/:
node --import tsx --test tests/copytrading-oracle.test.ts
Then sign in to the stand UI and start copytrading-regression, or call the authenticated control endpoint with the selected snapshot:
curl -X POST http://127.0.0.1:8095/api/scenarios/copytrading-regression/run \
-H 'Origin: http://127.0.0.1:8095' \
-b 'stand_session=<cookie-from-sign-in>' \
-H 'Content-Type: application/json' \
-d '{"snapshotId":"clean-base"}'
The stand restores the chosen snapshot before fixture setup. Follow run status, assertions and checkpoints in the stand report; do not infer success from an accepted request or from record persistence alone. Local compilation and test execution are restricted by the server repository policy. The implementation has not been remotely certified until the remote helper test and scenario run complete successfully.