DXtrade platform for Tradeify247: percent fees, hard daily-loss stops, volatility sizing, every-day lanes
- DXtrade client (lib/dxtrade-class.ts): REST session login with lockout-safe backoff, 60s sync of accounts/metrics/positions/orders/transfers, history and fund transactions from cash transactions, day-start balance at the 22:00 UTC reset, CLOSE-by-positionCode exits (native OCO group, else a stop-first emulated pair), a cross-account hedge guard, stray-order reaper. - Instrument selector: the firm's symbol map lists candidate symbols per CME root; the cheapest tradable one per unit of recent hourly volatility wins (spread + 2x commission [+ financing] / sigma). - Fees: a firm's commission % of notional prices every fee off the fill; the take-profit is solved in closed form so the leg nets its target. - Hard daily loss limit per account type: every leg carries a stop keeping the firm's day inside the limit less a 10% reserve and both fees (rounded in); a green day never widens it; no room or a refused bracket flattens. - Notional sizing where the platform prices positions: target-hold volatility sizing or the symbol map's fixed notional, under a per-type notional cap. - Every-day firms trade weekends/holidays; the scheduler runs lanes (futures, one per DXtrade firm) with their own hours, flat gate, mutex and batches. - The catalog sync skips platforms this build doesn't know instead of treating them as Tradovate. One shared fireLeg replaces the duplicated batch/copy code. - npm run check: fee/stop/sizing/hours/selector checks; scripts/dxtrade-smoke.mjs confirms the API against a real login before trading. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
49c4725e0c
commit
28f465508d
+32
-24
@@ -6,7 +6,8 @@ import { getAccountConfig, getInferredSize, matchAccountType } from '@/lib/accou
|
||||
import { historyFresh } from '@/lib/platform-client';
|
||||
import {
|
||||
combineCmeSessions, computeDayPlan, cycleCompletion, resolveEffectiveConfig, currentTradeDateCT,
|
||||
BIG_DAY_THRESHOLD, FIRST_DAY_STOP, LEG_COMPLETION_EPSILON, MIN_DAY_STOP_MULT,
|
||||
obfuscationStopDollars, dllRoom as computeDllRoom, dllStopDollars, botStopsActive,
|
||||
LEG_COMPLETION_EPSILON,
|
||||
} from '@/lib/trading-logic';
|
||||
|
||||
export async function GET() {
|
||||
@@ -84,6 +85,7 @@ export async function GET() {
|
||||
let remainingToday: number | null = null;
|
||||
let effectiveProfitTarget: number | null = null;
|
||||
let nextStop: number | null = null;
|
||||
let dllRoom: number | null = null;
|
||||
if (cfg && effective && !isDead) {
|
||||
const equityProfit = cash.amount - cfg.accountSize;
|
||||
const plan = computeDayPlan(
|
||||
@@ -94,25 +96,29 @@ export async function GET() {
|
||||
dailyTarget = plan ? { amount: plan.dayTarget, path: plan.path } : null;
|
||||
remainingToday = plan?.remainingToday ?? null;
|
||||
|
||||
// The stop the next trade would carry under the obfuscation
|
||||
// options, mirroring obfuscationStopDollars in auto-trade:
|
||||
// day 1 keeps the day's net floor at −$1,000, min days stop
|
||||
// at 2× the target, and the day after a loss rides stopless.
|
||||
if (plan && (cfg.firstDayStop || cfg.minDayStop)) {
|
||||
const prior = dailyPnL
|
||||
.filter((d) => d.date < combined.tradeDate && d.pnl !== 0)
|
||||
.sort((a, b) => a.date.localeCompare(b.date));
|
||||
const recoveryDay = prior.length > 0 && prior[prior.length - 1].pnl < 0;
|
||||
if (!recoveryDay) {
|
||||
let stop = Infinity;
|
||||
if (cfg.firstDayStop && prior.length === 0) {
|
||||
stop = Math.max(0, Math.round((FIRST_DAY_STOP + combined.realizedPnL) * 100) / 100);
|
||||
}
|
||||
if (cfg.minDayStop && plan.dayTarget <= BIG_DAY_THRESHOLD) {
|
||||
stop = Math.min(stop, MIN_DAY_STOP_MULT * plan.dayTarget);
|
||||
}
|
||||
if (Number.isFinite(stop) && stop > 0) nextStop = Math.round(stop * 100) / 100;
|
||||
}
|
||||
// The stop the next trade would carry, from the same helpers
|
||||
// the fire path uses: the obfuscation stop (day 1 keeps the
|
||||
// day's net floor at −$1,000, min days stop at 2× the leg,
|
||||
// the day after a loss rides stopless) against the hard
|
||||
// daily-loss stop, before fees. A min day trades its whole
|
||||
// remaining target as one leg.
|
||||
const dll = cfg.dailyLossLimit ?? 0;
|
||||
if (dll > 0) {
|
||||
const dayStart = client.dayStartBalance?.(acc.id);
|
||||
dllRoom = dayStart === null ? null : computeDllRoom({
|
||||
dailyLossLimit: dll,
|
||||
closedBalance: cash.amount,
|
||||
dayStartBalance: dayStart,
|
||||
realizedToday: Math.min(cash.realizedPnL, combined.realizedPnL),
|
||||
});
|
||||
}
|
||||
if (plan && (botStopsActive(cfg))) {
|
||||
const obf = obfuscationStopDollars(cfg, dailyPnL, plan, combined.tradeDate, plan.remainingToday);
|
||||
const hard = dllRoom !== null
|
||||
? dllStopDollars({ room: dllRoom, dailyLossLimit: dll, entryFee: 0, commissionPct: 0 })
|
||||
: Infinity;
|
||||
const stop = Math.min(Number.isFinite(obf) && obf > 0 ? obf : Infinity, hard);
|
||||
if (Number.isFinite(stop) && stop > 0) nextStop = Math.round(stop * 100) / 100;
|
||||
}
|
||||
|
||||
// Effective profit target = max(stage target, consistency realTarget).
|
||||
@@ -187,6 +193,7 @@ export async function GET() {
|
||||
cycleDailyPnL: client.dailyPnL[acc.id],
|
||||
rawRealizedPnL: cash.realizedPnL,
|
||||
combineCmeHolidays: cfg.combineCmeHolidays,
|
||||
tradesEveryDay: cfg.tradesEveryDay === true,
|
||||
effective,
|
||||
equityProfit: cash.amount - cfg.accountSize,
|
||||
splitEnabled: cfg.splitEnabled,
|
||||
@@ -202,10 +209,10 @@ export async function GET() {
|
||||
// on the Tuesday after a holiday this includes Sun/Mon.
|
||||
realizedPnL: combined.realizedPnL,
|
||||
daysTraded,
|
||||
// A stop-enabled account whose day booked red via its own
|
||||
// stop — the personal DLL. Firm DLL lockouts show via
|
||||
// !active instead.
|
||||
personalDll: !!cfg && (cfg.firstDayStop || cfg.minDayStop)
|
||||
// An account the bot puts stops on whose day booked red via
|
||||
// its own stop — the personal DLL. Firm DLL lockouts show
|
||||
// via !active instead.
|
||||
personalDll: !!cfg && botStopsActive(cfg)
|
||||
&& acc.active && !client.positions[acc.id]
|
||||
&& combined.realizedPnL <= -25,
|
||||
positionDirection: client.positions[acc.id]
|
||||
@@ -220,6 +227,7 @@ export async function GET() {
|
||||
stageDrain: effective?.drain ?? false,
|
||||
dailyTarget,
|
||||
nextStop,
|
||||
dllRoom,
|
||||
remainingToday,
|
||||
effectiveProfitTarget,
|
||||
dailyPnL,
|
||||
|
||||
Reference in New Issue
Block a user