Skip to content

Commit c52dac5

Browse files
committed
chore: bump to v1.1.2 — AMA refit, optimizer chart output, constant tuning
## AMA Constants Refit (pool 133 1h, λ=0.0022/step=0.0002) - AMA1: slowPeriod 93.1→80.6, AMA2: 102.4→84.6, AMA3: 112.7→93.1, AMA4: 136.4→107.4 - DYNAMIC_WEIGHT_AMA_MAX_SLOPE_PCT: 0.08→0.085 ## Optimizer Improvements - Auto-generates interactive HTML chart via lp_chart_core alongside results JSON - Output filenames include λ/step suffix for multi-run comparison - Fixed tsx v4 strip-mode compat (export= → module.exports) - Removed hardcoded REPOS_THRESHOLD=0.004; wired trackRepositions to production AMA_DELTA_THRESHOLD_PERCENT - Restored DISTANCE_WEIGHT_STEP=0.0002 so AMA1-4 form a distinct tight-to-loose spectrum ## amaS% Knob (dynamic weight chart) - Range narrowed from 0.01-0.5 to 0.04-0.12 ## Docs - Swept outdated AMA slow values (112.7→93.1), tsx→node dist commands, amaS% research range ## Testing - tsc compiles cleanly, optimizer runs on pool 133 1h, chart generator produces valid HTML
1 parent 89e8f6f commit c52dac5

21 files changed

Lines changed: 186 additions & 193 deletions

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [1.1.2] - 2026-07-13 - AMA Optimizer Improvements, Constants Tuning & Doc Sync
6+
7+
### 2026-07-13
8+
9+
- **Feat**: optimizer now auto-generates an interactive HTML chart alongside results JSON — uses `generateHTML` from `lp_chart_core` to render the four winning AMAs against LP price data (`analysis/ama_fitting/optimizer_high_resolution.ts`).
10+
- **Feat**: output filenames include λ and step suffix (e.g. `_l0_0022_s0_0002`) for easy comparison across runs (`analysis/ama_fitting/optimizer_high_resolution.ts`).
11+
- **Feat**: `export =` replaced with `module.exports =` in optimizer to fix `tsx v4` strip-only mode compatibility (`analysis/ama_fitting/optimizer_high_resolution.ts`).
12+
- **Fix**: hardcoded `REPOS_THRESHOLD = 0.004` (0.4%) removed — `calcReposRate` replaced by `trackRepositions` wired to `MARKET_ADAPTER.AMA_DELTA_THRESHOLD_PERCENT`. Analyzer now uses the production threshold (`analysis/ama_fitting/optimizer_high_resolution.ts`, `analysis/ama_fitting/analyze_ama_price_changes.ts`).
13+
- **Fix**: `trackRepositions` extracted to shared utility then inlined back as a simplified single-threshold function — removed untracked shared file, kept logic in `analyze_ama_price_changes.ts`.
14+
- **Fix**: all four AMA cap quantiles produced identical winners with constant λ — re-enabled `DISTANCE_WEIGHT_STEP = 0.0002` so λ varies per AMA for a clean tight-to-loose spectrum (`analysis/ama_fitting/optimizer_high_resolution.ts`).
15+
- **Tune**: AMA1–AMA4 slow periods refitted on pool 133 1h data (2023-05 → 2026-05) with optimizer λ=0.0022/step=0.0002. Results: AMA1=80.6, AMA2=84.6, AMA3=93.1, AMA4=107.4 (`modules/constants.ts`).
16+
- **Tune**: `BASE_DISTANCE_WEIGHT` set to 0.0022, `DYNAMIC_WEIGHT_AMA_MAX_SLOPE_PCT` set to 0.085, amaS% knob range narrowed to 0.04–0.12 (`analysis/trend_detection/dynamic_weight_chart_generator.ts`, `modules/constants.ts`).
17+
- **Docs**: sweep AMA slow values (112.7→93.1), tsx→node dist command updates, unified comparison chart notes, and amaS% research range in 5 documentation files.
18+
- **Chore**: add `"tsx": { "tsconfig": true }` to root `package.json` (mitigation for `export =` in tsx v4).
19+
- **Chore**: version bumped to 1.1.2 across all manifests.
20+
521
## [1.1.1] - 2026-07-12 - Auto-startup Migration, Error Handling & Cleanup
622

723
### 2026-07-12

analysis/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Tools that inspect DEXBot trading behavior and the market data it operates on. O
2727
| [`analyze_volatility.ts`](#volatility-analyze_volatilityts) | "Both weights clipped too hard / not enough?" | `tsx analysis/analyze_volatility.ts --bot-key <bot-key>` |
2828
| [`analyze_regime.ts`](#supporting-sub-signals) | "Is the trend/chaos gate too aggressive?" | `tsx analysis/analyze_regime.ts --bot-key <bot-key>` |
2929
| [`analyze_kalman.ts`](#supporting-sub-signals) | "Is Kalman's contribution to the blend right?" | `tsx analysis/analyze_kalman.ts --bot-key <bot-key>` |
30-
| [`ama_fitting/`](#ama_fitting) | "Which AMA preset fits this market?" | `tsx analysis/ama_fitting/optimizer_high_resolution.ts --data <lp-file>` |
30+
| [`ama_fitting/`](#ama_fitting) | "Which AMA preset fits this market?" | `node dist/analysis/ama_fitting/optimizer_high_resolution.js --data <lp-file>` |
3131
| [`bot_fitting/`](#bot_fitting) | "What spread / increment / ratio for my grid?" | `tsx analysis/bot_fitting/backtest_ama_sweep.ts --data <lp-file>` |
3232

3333
> `analyze_derivatives.ts` (SMA / MACD / RSI derivative layer) is legacy and not surfaced — kept for reference only.
@@ -355,7 +355,7 @@ AMA parameter optimization and comparison tools.
355355
| Script | Purpose |
356356
|--------|---------|
357357
| `optimizer_high_resolution.ts` | AMA parameter optimizer (erPeriod, fast/slow bounds) |
358-
| `generate_unified_comparison_chart.ts` | AMA comparison chart across multiple parameter sets |
358+
| `generate_unified_comparison_chart.ts` | AMA comparison chart (defaults from constants, use optimizer for fitted params) |
359359
| `analyze_ama_price_changes.ts` | AMA price-change analysis |
360360
| `fetch_lp_candles.ts` | LP candle data fetcher |
361361
| `calibrate_convergence_er.ts` | Calibrate AMA_CONVERGENCE_ER_AVG from LP data |
@@ -429,7 +429,7 @@ These npm scripts wrap common analysis runners:
429429
|--------|---------|
430430
| `npm run analysis:tradingview` | `tsx analysis/tradingview/analyze_tradingview.ts` |
431431
| `npm run analysis:trade-pnl` | `tsx analysis/trade_profitability.ts` |
432-
| `npm run ama:chart:lp-local` | `tsx analysis/ama_fitting/generate_unified_comparison_chart.ts` |
432+
| `npm run ama:chart:lp-local` | `tsx analysis/ama_fitting/generate_unified_comparison_chart.ts` (chart also auto-generated by optimizer) |
433433

434434
All accept `--` forwarded flags.
435435

analysis/ama_fitting/README.md

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -64,36 +64,46 @@ Output: `market_adapter/data/lp/<pair_folder>/lp_pool_<poolShort>_<interval>.jso
6464

6565
`optimizer_high_resolution.ts` runs a parallel geometric grid search over
6666
ER × Fast × Slow combinations. Produces four AMA winners (AMA1–AMA4) using
67-
different distance-cap quantiles and writes results to a JSON file.
67+
different distance-cap quantiles and writes results to a JSON file + auto-generates
68+
an interactive HTML chart.
6869

6970
By default this does **not** update runtime market-adapter profiles. Add
7071
`--write-profiles` when you intentionally want the fitted parameters exported
7172
to `profiles/market_profiles.json`.
7273

7374
**Run on the fetched LP data:**
7475
```bash
75-
tsx analysis/ama_fitting/optimizer_high_resolution.ts \
76+
npm run build && node dist/analysis/ama_fitting/optimizer_high_resolution.js \
7677
--data market_adapter/data/lp/<pair>/lp_pool_<id>_<interval>.json
7778
```
7879

80+
Or from the `analysis/ama_fitting/` directory using the local script shortcut:
81+
```bash
82+
npm run optimize -- --data ../../market_adapter/data/lp/<pair>/lp_pool_<id>_<interval>.json
83+
```
84+
7985
**Export winners to the market adapter profile file:**
8086
```bash
81-
tsx analysis/ama_fitting/optimizer_high_resolution.ts \
87+
npm run build && node dist/analysis/ama_fitting/optimizer_high_resolution.js \
8288
--data market_adapter/data/lp/<pair>/lp_pool_<id>_<interval>.json \
8389
--write-profiles
8490
```
8591

92+
> `npx tsx` does not work on this project due to `export =` syntax in dependency
93+
> modules (unsupported in tsx v4 strip-only mode). Always use the compiled output
94+
> via `node dist/...` or the `npm run optimize` shortcut.
95+
8696
**Default search ranges:**
8797

8898
| Param | Min | Max | Sampling | Quantum |
8999
|-------|------|------|----------|---------|
90-
| ER | 50 | 500 | 40 geometric points | 1 |
91-
| Fast | 1 | 10 | 40 geometric points | 0.01 |
92-
| Slow | 500 | 5000 | 40 geometric points | 1 |
100+
| ER | 500 | 1000 | 15 geometric points | 1 |
101+
| Fast | 2 | 8 | 30 geometric points | 0.01 |
102+
| Slow | 50 | 200 | 30 geometric points | 0.1 |
93103

94104
Override ranges via CLI:
95105
```bash
96-
tsx analysis/ama_fitting/optimizer_high_resolution.ts \
106+
npm run build && node dist/analysis/ama_fitting/optimizer_high_resolution.js \
97107
--data market_adapter/data/lp/<pair>/lp_pool_<id>_<interval>.json \
98108
--erMin 100 --erMax 600 \
99109
--slowMin 800 --slowMax 6000
@@ -138,17 +148,23 @@ Source: pool 133 `IOB.XRP/BTS`, 1h candles, 2023-05-05 22:00 UTC through
138148
2026-05-04 22:00 UTC.
139149

140150
**Outputs:**
141-
- `analysis/ama_fitting/optimization_results_<datafile>.json` — full results
151+
- `analysis/ama_fitting/optimization_results_<datafile>_l<λ>_s<step>.json` — full results (e.g. `_l0020_s000` for λ=0.0020, step=0)
152+
- `analysis/charts/optimization_chart_<datafile>_l<λ>_s<step>.html` — interactive AMA overlay chart (auto-generated)
142153
- `profiles/market_profiles.json` — updated with new AMA parameters per pair only when `--write-profiles` is used
143154

144155
**Boundary check:** If a winner lands on the edge of the search range, the
145156
optimizer warns you. Widen the affected range and re-run.
146157

147158
---
148159

149-
## Step 3 — Visual Review (optional)
160+
## Step 3 — Visual Review
161+
162+
A chart is auto-generated during Step 2 at
163+
`analysis/charts/optimization_chart_<datafile>.html`. Open it in a browser
164+
to compare all four optimized AMA overlays against the candlestick price.
150165

151-
Recommended entrypoint:
166+
For a standalone chart without re-running the optimizer (uses current defaults
167+
from `modules/constants.ts` rather than optimized results):
152168

153169
```bash
154170
npm run lp:chart -- \
@@ -159,19 +175,6 @@ This generates both:
159175
- `analysis/charts/lp_AMA_chart_pool_133.html` — market-adapter style LP chart
160176
- `analysis/charts/lp_chart_1h_UNIFIED_COMPARISON.html` — unified comparison chart, with the interval derived from LP metadata
161177

162-
Under the hood, LP-data chart generation now delegates into the shared runner in
163-
`market_adapter/lp_chart_runner.ts`. The analysis script keeps only:
164-
- the local LP comparison mode used for analysis-only review
165-
166-
Local LP comparison entrypoint:
167-
168-
```bash
169-
npm run ama:chart:lp-local -- \
170-
--data market_adapter/data/lp/<pair>/lp_pool_<id>_<interval>.json
171-
```
172-
173-
Open the generated HTML in a browser to compare all four AMA overlays against the candlestick price.
174-
175178
---
176179

177180
## How market_profiles.json is used

analysis/ama_fitting/analyze_ama_price_changes.ts

Lines changed: 52 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,8 @@
66
* Simulates AMA_DELTA_THRESHOLD_PERCENT grid-reposition logic for all four AMA
77
* series on LP candle data.
88
*
9-
* For each threshold (1%, 2%, 3%, 4%):
10-
* - Set a baseline at the first post-warmup AMA value
11-
* - Count candle steps until the AMA drifts ≥ threshold from that baseline
12-
* - Record the reposition event, reset baseline to current AMA value
13-
* - Repeat for the full live window
14-
*
15-
* Reports: reposition count, avg/min/max steps between repositions, frequency
16-
* per 1000 live steps — for each AMA × threshold combination.
9+
* Uses the production threshold from MARKET_ADAPTER.AMA_DELTA_THRESHOLD_PERCENT
10+
* (modules/constants.ts).
1711
*
1812
* Usage:
1913
* tsx analysis/ama_fitting/analyze_ama_price_changes.ts --data <path-to-lp-candles.json> --results <path-to-optimization-results.json>
@@ -22,7 +16,39 @@ const fs = require('fs');
2216
const path = require('path');
2317
const { calculateAMA } = require('../../market_adapter/core/strategies/ama');
2418
const { readJSON } = require('../../modules/utils/fs_utils');
25-
const THRESHOLDS = [1, 2, 3, 4]; // percent
19+
const { MARKET_ADAPTER } = require('../../modules/constants');
20+
const REPOS_THRESHOLD_PCT = MARKET_ADAPTER.AMA_DELTA_THRESHOLD_PERCENT;
21+
22+
/**
23+
* Simulate AMA_DELTA_THRESHOLD_PERCENT reposition logic.
24+
*
25+
* Starting from the first post-warmup AMA value, track cumulative drift from
26+
* the last reposition baseline. When drift ≥ threshold, a reposition fires:
27+
* record step-count since previous reposition, reset baseline to current AMA.
28+
*/
29+
function trackRepositions(amaValues, thresholdPct, warmup) {
30+
let events = 0;
31+
const steps = [];
32+
let baseline = amaValues[warmup];
33+
let stepCounter = 0;
34+
for (let i = warmup + 1; i < amaValues.length; i++) {
35+
const curr = amaValues[i];
36+
if (baseline === 0) continue;
37+
stepCounter++;
38+
const driftPct = Math.abs((curr - baseline) / baseline) * 100;
39+
if (driftPct >= thresholdPct) {
40+
events++;
41+
steps.push(stepCounter);
42+
baseline = curr;
43+
stepCounter = 0;
44+
}
45+
}
46+
const min = steps.length > 0 ? Math.min(...steps) : 0;
47+
const max = steps.length > 0 ? Math.max(...steps) : 0;
48+
const avg = steps.length > 0 ? steps.reduce((a, b) => a + b, 0) / steps.length : 0;
49+
return { events, steps, min, max, avg };
50+
}
51+
2652
// ── Load data ─────────────────────────────────────────────────────────────────
2753
function loadData(filePath) {
2854
const json = readJSON(filePath);
@@ -44,57 +70,7 @@ function loadAmaParams(resultsPath) {
4470
];
4571
}
4672
// ── Analysis ──────────────────────────────────────────────────────────────────
47-
/**
48-
* Simulate AMA_DELTA_THRESHOLD_PERCENT reposition logic.
49-
*
50-
* Starting from the first post-warmup AMA value, track cumulative drift from
51-
* the last reposition baseline. When drift reaches a threshold, a reposition
52-
* fires: record the step-count since the previous reposition, then reset the
53-
* baseline to the current AMA value.
54-
*
55-
* Returns { threshold -> { events, steps: number[], min, max, avg } }
56-
* events — total reposition count
57-
* steps — candle steps between consecutive repositions
58-
* min/max/avg — statistics on those step-counts
59-
*/
60-
function trackRepositions(amaValues, thresholds, erPeriod) {
61-
const result = {};
62-
for (const t of thresholds) {
63-
result[t] = { events: 0, steps: [], min: Infinity, max: 0, avg: 0 };
64-
}
65-
// Each threshold has its own independent baseline + step counter
66-
const baselines = {};
67-
const stepCounters = {};
68-
for (const t of thresholds) {
69-
baselines[t] = amaValues[erPeriod]; // first post-warmup value
70-
stepCounters[t] = 0;
71-
}
72-
for (let i = erPeriod + 1; i < amaValues.length; i++) {
73-
const curr = amaValues[i];
74-
for (const t of thresholds) {
75-
const base = baselines[t];
76-
if (base === 0) continue;
77-
stepCounters[t]++;
78-
const driftPct = Math.abs((curr - base) / base) * 100;
79-
if (driftPct >= t) {
80-
const r = result[t];
81-
r.events++;
82-
r.steps.push(stepCounters[t]);
83-
baselines[t] = curr;
84-
stepCounters[t] = 0;
85-
}
86-
}
87-
}
88-
// Compute stats
89-
for (const t of thresholds) {
90-
const r = result[t];
91-
if (r.steps.length === 0) { r.min = 0; r.avg = 0; continue; }
92-
r.min = Math.min(...r.steps);
93-
r.max = Math.max(...r.steps);
94-
r.avg = r.steps.reduce((a, b) => a + b, 0) / r.steps.length;
95-
}
96-
return result;
97-
}
73+
9874
// ── Main ──────────────────────────────────────────────────────────────────────
9975
function run() {
10076
const dataArgIdx = process.argv.indexOf('--data');
@@ -123,67 +99,43 @@ function run() {
12399
console.log('════════════════════════════════════════════════════════════════════════════════');
124100
console.log(` Dataset: ${label} (${interval} candles)`);
125101
console.log(` Candles: ${candles.length}${totalSteps} steps total`);
126-
console.log(` Thresholds: ${THRESHOLDS.map(t => `${t}%`).join(' ')}`);
102+
console.log(` Threshold: ${REPOS_THRESHOLD_PCT}% (from MARKET_ADAPTER.AMA_DELTA_THRESHOLD_PERCENT)`);
127103
console.log('');
128104
console.log(' Logic: set baseline at warmup end, count steps until AMA drifts ≥ threshold,');
129105
console.log(' record reposition + reset baseline. Repeat for full live window.');
130106
console.log('');
131-
const cW = 10;
132-
const allResults = [];
107+
const results = [];
133108
for (const params of amaParams) {
134109
const values = calculateAMA(closes, {
135110
erPeriod: params.er,
136111
fastPeriod: params.fast,
137112
slowPeriod: params.slow,
138113
});
139-
const repoData = trackRepositions(values, THRESHOLDS, params.er);
114+
const r = trackRepositions(values, REPOS_THRESHOLD_PCT, params.er);
140115
const liveSteps = totalSteps - params.er;
141-
const suffix = String(params.label).replace(/^AMA\d\s*/i, '').replace(/^[-:\s]+/, '').trim();
116+
const freq = liveSteps > 0 ? (r.events / liveSteps * 1000).toFixed(1) : '–';
117+
const avg = r.events > 0 ? r.avg.toFixed(1) : '–';
118+
const suffix = String(params.label).replace(/^AMA\d\s*/i, '').replace(/^[-:\s]+/, '').trim();
142119
const rowLabel = `${params.key} ${suffix}`;
143120
console.log(` ── ${rowLabel} (warmup: ${params.er} live: ${liveSteps} steps) ──`);
144-
console.log(
145-
' ' +
146-
'threshold'.padEnd(12) +
147-
'repositions'.padStart(cW) +
148-
'avg steps'.padStart(cW) +
149-
'min steps'.padStart(cW) +
150-
'max steps'.padStart(cW) +
151-
' /1000 steps'
152-
);
153-
console.log(' ' + '─'.repeat(12 + cW * 4 + 12));
154-
for (const t of THRESHOLDS) {
155-
const r = repoData[t];
156-
const freq = liveSteps > 0 ? (r.events / liveSteps * 1000).toFixed(1) : '–';
157-
const avg = r.events > 0 ? r.avg.toFixed(1) : '–';
158-
const min = r.events > 0 ? String(r.min) : '–';
159-
const max = r.events > 0 ? String(r.max) : '–';
160-
console.log(
161-
' ' +
162-
`≥${t}%`.padEnd(12) +
163-
String(r.events).padStart(cW) +
164-
avg.padStart(cW) +
165-
min.padStart(cW) +
166-
max.padStart(cW) +
167-
` ${freq}`
168-
);
169-
allResults.push({
170-
label: `${params.key}${t}%`,
171-
events: r.events,
172-
avg: r.avg,
173-
freq: liveSteps > 0 ? r.events / liveSteps * 1000 : 0,
174-
});
175-
}
121+
console.log(` ≥${REPOS_THRESHOLD_PCT}% repositions: ${r.events} avg steps: ${avg} min: ${r.events > 0 ? r.min : '–'} max: ${r.events > 0 ? r.max : '–'} freq: ${freq}/1k steps`);
176122
console.log('');
123+
results.push({
124+
label: rowLabel,
125+
events: r.events,
126+
avg: r.avg,
127+
freq: liveSteps > 0 ? r.events / liveSteps * 1000 : 0,
128+
});
177129
}
178130
console.log(' Note: warmup candles excluded (AMA initializes from SMA of the ER window).');
179131
console.log('');
180132
// ── Ranking: fewest repositions ────────────────────────────────────────────
181-
allResults.sort((a, b) => a.events - b.events);
133+
results.sort((a, b) => a.events - b.events);
182134
console.log(' Ranking — fewest repositions (least grid changes):');
183135
console.log('');
184-
console.log(' ' + '#'.padEnd(4) + 'AMA + threshold'.padEnd(20) + 'repositions'.padStart(12) + 'avg steps'.padStart(10) + ' /1000 steps');
136+
console.log(' ' + '#'.padEnd(4) + 'AMA'.padEnd(20) + 'repositions'.padStart(12) + 'avg steps'.padStart(10) + ' /1000 steps');
185137
console.log(' ' + '─'.repeat(4 + 20 + 12 + 10 + 12));
186-
allResults.forEach((r, i) => {
138+
results.forEach((r, i) => {
187139
console.log(
188140
' ' +
189141
String(i + 1).padEnd(4) +

0 commit comments

Comments
 (0)