Skip to content

Commit 5b32b96

Browse files
[cli] validate configuration consistency on grid reconciliation (#48)
1 parent 1ed0ef1 commit 5b32b96

1 file changed

Lines changed: 46 additions & 25 deletions

File tree

cli/src/engine/grid-bot.ts

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,51 @@ export class ForegroundGridBot {
194194
);
195195
}
196196

197+
if (
198+
!new Decimal(existingState.config.investment).eq(
199+
this._config.investment,
200+
)
201+
) {
202+
throw new Error(
203+
`Saved grid was started with investment ${existingState.config.investment} ` +
204+
`but you requested ${this._config.investment}. ` +
205+
`Use --reset to discard saved state and start fresh.`,
206+
);
207+
}
208+
209+
if (existingState.config.dryRun !== this._config.dryRun) {
210+
throw new Error(
211+
`Saved grid was started in ${existingState.config.dryRun ? "dry-run" : "live"} mode ` +
212+
`but you requested ${this._config.dryRun ? "dry-run" : "live"} mode. ` +
213+
`Use --reset to discard saved state and start fresh.`,
214+
);
215+
}
216+
217+
if (
218+
Boolean(existingState.config.trailingUp) !== this._config.trailingUp
219+
) {
220+
throw new Error(
221+
`Saved grid was started ${existingState.config.trailingUp ? "with" : "without"} --trailing-up ` +
222+
`but you requested ${this._config.trailingUp ? "with" : "without"} --trailing-up. ` +
223+
`Use --reset to discard saved state and start fresh.`,
224+
);
225+
}
226+
227+
const savedStopLoss = existingState.config.stopLoss;
228+
const newStopLoss = this._config.stopLoss;
229+
const stopLossMismatch =
230+
(savedStopLoss == null) !== (newStopLoss == null) ||
231+
(savedStopLoss != null &&
232+
newStopLoss != null &&
233+
!new Decimal(savedStopLoss).eq(newStopLoss));
234+
if (stopLossMismatch) {
235+
throw new Error(
236+
`Saved grid was started with stop-loss ${savedStopLoss ?? "(none)"} ` +
237+
`but you requested ${newStopLoss ?? "(none)"}. ` +
238+
`Use --reset to discard saved state and start fresh.`,
239+
);
240+
}
241+
197242
await this._reconcileAndInit(existingState);
198243
} else {
199244
await this._initNewGrid();
@@ -1079,17 +1124,14 @@ export class ForegroundGridBot {
10791124
// Phase 1: Adopt saved state as-is
10801125
this._state = savedState;
10811126

1082-
// Update mutable config fields (geometry + split validated in run())
1127+
// Update mutable config fields (everything else validated in run())
10831128
this._state.config.intervalSec = config.intervalSec;
1084-
this._state.config.dryRun = config.dryRun;
10851129

10861130
const quoteStep = this._getQuoteStep();
10871131
const baseStep = this._getBaseStep();
10881132
this._state.quotePrecision = quoteStep.toString();
10891133
this._state.basePrecision = baseStep.toString();
10901134

1091-
const newInvestment = new Decimal(config.investment);
1092-
10931135
// Phase 2: Verify each saved order against the exchange
10941136
let buysFilled = 0;
10951137
let sellsFilled = 0;
@@ -1210,27 +1252,6 @@ export class ForegroundGridBot {
12101252
}
12111253
}
12121254

1213-
// Recalculate quotePerLevel if investment changed
1214-
if (config.investment !== this._state.config.investment) {
1215-
const midPrice = await this._getMidPrice();
1216-
const totalActiveLevels = this._state.levels.filter(
1217-
(l) =>
1218-
l.buyOrderIds.length > 0 ||
1219-
l.positions.length > 0 ||
1220-
new Decimal(l.price).lte(midPrice),
1221-
).length;
1222-
const quotePerLevel = newInvestment
1223-
.div(Math.max(totalActiveLevels, 1))
1224-
.toDecimalPlaces(2, Decimal.ROUND_DOWN);
1225-
this._state.quotePerLevel = quotePerLevel.toString();
1226-
this._state.config.investment = config.investment;
1227-
console.log(
1228-
chalk.dim(
1229-
` Investment changed: quote per level recalculated to ${quotePerLevel}`,
1230-
),
1231-
);
1232-
}
1233-
12341255
// Phase 3: Handle split mode
12351256
if (
12361257
config.splitInvestment &&

0 commit comments

Comments
 (0)