# Compliance & Safety Check: 'Strongest Few' ETF rotation (synthetic paper simulation)

Reviewer: Compliance Officer · Date: 2026-10-09 · Scope: every file in the workspace listed under §5 "Files read".

## Decision

**APPROVED as an internal, offline experiment.** Nothing here sends orders, holds an account, spends money or fetches data, so it can keep running as it is.

**CONDITIONAL for any external use.** Before the report, CSV or numbers are shown to anyone outside the project (blog, social media, a pitch, a client, a marketplace), fixes F1 to F3 in §3 must be made. F4 applies only when the project moves to real market data.

| Check | Result | Basis |
|---|---|---|
| No private or personal data used | **PASS** | See §1.1 |
| No real money at risk (paper/synthetic only) | **PASS** | See §1.2 |
| Code does not ask for live trading API keys | **PASS** | See §1.3 |
| All outputs carry the needed disclaimers | **PARTIAL** | See §2 and fixes F1 to F3 |

---

## 1. What was verified in the code

### 1.1 No private data: PASS
- `repro.py` creates every price itself with `random.Random(seed)` inside `generate_prices()`. The only inputs are the seed and four ETF tickers plus SPY, set in `strategy_spec.py` (`TICKERS = ["SPY","QQQ","IWM","EFA","EEM"]`).
- I found no names, emails, account numbers or any other personal fields in `repro.py`, `strategy_spec.py`, `return_calculation.py`, `tests/*.py`, `README.md`, `assumptions.md`, `backtest_plan.md`, `validation_report.txt`, or the header and first rows of `backtest_results.csv`.
- The CSV columns are `date, strategy_nav, strategy_return, spy_nav, spy_return, holdings, turnover`. All of them are simulated.

### 1.2 No real money at risk: PASS
- No module contains order placement, broker or exchange client code, or wallet/payment logic.
- In `run_backtest()`, the portfolio is a starting NAV of `1.0` (a unitless fraction, not dollars). Trades are just reassignments of the `shares`/`cash` variables in memory.
- The only things written to disk are `backtest_results.csv` and `validation_report.txt`, in `--out-dir` (`main()` in `repro.py`).

### 1.3 No live trading API keys requested: PASS
I read the code line by line, so these statements cover the files listed in §5 only:
- **Imports in `repro.py`:** `argparse, csv, datetime, math, os, random, statistics`, plus the local modules `return_calculation` and `strategy_spec`. It imports no `requests`, `urllib`, `http`, `socket`, `yfinance`, `ccxt`, `alpaca`, `ib_insync` or any broker SDK.
- **Environment and secrets:** `os` is used only for `os.makedirs` and `os.path.join`. Nothing calls `os.environ` or `os.getenv`, and no `.env` file is read. The CLI flags are only `--seed`, `--years`, `--seeds` and `--out-dir` (`parse_args()`). No key, token, account ID or endpoint argument exists.
- **Other imports:** `return_calculation.py` imports `math`, `typing`, and `sys`/`time` inside `_run_tests()`. `strategy_spec.py` contains only constants.
- **Offline test:** `tests/test_repro.py::test_main_runs_ten_years_offline` replaces `socket.socket` with a function that raises an error, then runs `repro.main()` for the full 10 years. If that test passes, the full run made no network access.
- **Limitation:** I did not run the tests myself because I have no code-execution tool. The claim that "10 tests pass" comes from the earlier task's result and has not been checked again here.

---

## 2. Disclaimers: current state

| Output | Has a 'synthetic' / 'does not confirm the thesis' caveat? | Says 'hypothetical, not real trading, not investment advice'? |
|---|---|---|
| `validation_report.txt` | Yes. The closing caveat says the prices are synthetic, the generator was calibrated, and ALIGNED does not confirm the thesis. | **No** |
| `README.md` | Yes. The "These parameters were calibrated on purpose" section and the Caveat section. | **No** |
| `backtest_results.csv` | **No.** It has no disclaimer at all, and it uses real tickers (SPY, IWM…) with real-looking calendar dates starting 2015-01-02. | **No** |

**Why this matters.** Copied outside the project, the CSV would look like a real 2015-2024 SPY/QQQ/IWM backtest. The report's headline "VERDICT: ALIGNED, CAGR 35.78%, Sharpe 2.16" also comes from a generator that was tuned until the strategy hit the thesis numbers. Shown without a caveat, either one would be a misleading performance claim. Relevant rules:
- **FINRA Rule 2210(d)(1)**, https://www.finra.org/rules-guidance/rulebooks/finra-rules/2210. Communications must be fair and balanced and must not leave out material facts. (d)(1)(F) bans predicting or projecting performance and implying that past performance will recur, apart from narrow exceptions such as hypothetical illustrations of mathematical principles.
- **SEC Marketing Rule, 17 CFR 275.206(4)-1**, https://www.law.cornell.edu/cfr/text/17/275.206(4)-1. It bans untrue or unsubstantiated statements and misleading implications in adviser advertisements. It also defines "hypothetical performance" and attaches conditions to presenting it. The page digest I received did not reproduce the exact paragraph numbers, so I cite the rule as a whole. I did not verify the paragraph-level text, and the eCFR copy was blocked by a CAPTCHA.
- **FTC baseline (compliance-basics skill):** claims must be truthful and substantiated.

**Applicability (assumption).** We are neither a FINRA member nor a registered adviser, so these rules do not bind this project directly. I use them as the industry standard for presenting backtested and hypothetical results fairly, which matches our red line on truthful, substantiated claims.

---

## 3. Required fixes

**F1. Add a standard disclaimer to `validation_report.txt` (repro.py → `build_report`)**
Add these lines to the top of the `lines` list:
```
"HYPOTHETICAL RESULTS - SYNTHETIC DATA - PAPER SIMULATION ONLY.",
"No real money was traded and no real market data was used. Results come from a price",
"generator tuned to favour this strategy and do not represent past or future performance",
"of SPY, QQQ, IWM, EFA, EEM or any real portfolio. Not investment advice.",
```
Then run `python repro.py` again to regenerate the report. The existing test `assert "synthetic" in report` still passes. Add a test that asserts `"HYPOTHETICAL" in report`.

**F2. Make `backtest_results.csv` unmistakably synthetic (repro.py → `write_results_csv`)**
- Choose one of these options:
  - (a) Rename the output to `backtest_results_SYNTHETIC.csv`.
  - (b) Add a first column `data_source` with the value `synthetic_seed_<seed>` on every row.
- Then do both of the following:
  - Replace the real-looking calendar (`business_days(dt.date(2015,1,2), ...)`) with a day index (`sim_day` = 0…2519), or move the start to an obviously fictional base such as `2000-01-03` and label the column `synthetic_date`.
  - Rename the `spy_nav`/`spy_return` columns to `sim_spy_nav`/`sim_spy_return`.
- Update the test that checks the column set (`test_main_runs_ten_years_offline`) to match.

**F3. Add a disclaimer section to `README.md`**
Put the same 4-line text from F1 directly under the title. The existing caveat should stay.

**F4. Only before a real-data phase: data-source terms**
`backtest_plan.md` §1 suggests "`yfinance` or equivalent free API".
- The yfinance project states it is not affiliated with, endorsed by or vetted by Yahoo (https://github.com/ranaroussi/yfinance, Legal/disclaimer section).
- Yahoo's Terms of Service ban automated access (robots, spiders, scrapers) without express prior permission. They also ban commercial reuse of content without written permission (https://legal.yahoo.com/us/en/yahoo/terms/otos/index.html, the use-of-services and content restrictions).
- Fix: before any real-data run, use a data source whose licence explicitly allows programmatic access. Examples are a licensed vendor API or the ETF issuers' own downloadable NAV/price files, after checking their terms. Record the licence URL in `README.md`. Do not ship yfinance-based code in anything commercial.

**Not required, but recommended.** Report the less flattering seed distribution next to the main-seed result whenever results are quoted, not only further down the report. That means 21/30 seeds meeting both targets, a CAGR 5th percentile of 10.82%, and a Sharpe 5th percentile of 0.93.

---

## 4. Other red-line checks
- **Market manipulation, MNPI, wash trading:** not applicable. No orders are placed on any venue.
- **Email, outreach, platforms:** not applicable. Nothing is sent, listed or published.
- **Intellectual property:** the tickers SPY, QQQ, IWM, EFA and EEM are used only as nominal labels. I did not check the sponsors' trademark terms (State Street, Invesco, BlackRock/iShares). Using tickers as references is low risk in an internal simulation. Any public product name should avoid them.
- **Data access / robots.txt:** none happens in the current code (§1.3).

## 5. What was and was not checked
**Files read in full:**
- `repro.py`
- `strategy_spec.py`
- `return_calculation.py`
- `tests/test_repro.py`
- `tests/test_return_calculation.py`
- `tests/conftest.py`
- `README.md`
- `assumptions.md`
- `backtest_plan.md`
- `validation_report.txt`

**Read in part:** `backtest_results.csv`, first 600 characters only. The rest is produced by `write_results_csv()`, which I did read.

**Not checked:**
- `__pycache__/*.pyc` and `.pytest_cache/*`. These are compiled or cached files. I assume they match the source, but did not verify that.
- Whether the tests actually pass. I could not execute them.
- Exact paragraph text of SEC Rule 206(4)-1. The eCFR copy was CAPTCHA-blocked, and the Cornell digest gave only a summary.
- The SEC investor-alert page on automated investment tools (403 error, so it is not cited).
- ETF sponsor trademark terms.
- Any tax, licensing or registration question that would come up if this became a paid signal or advisory product. That would need its own review.
