/* ============================================== */
/* TraderVerdict Tool Styles */
/* ============================================== */
.tv-tool-wrapper {
font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;
font-feature-settings: 'ss01', 'cv11';
max-width: 960px;
margin: 40px auto;
padding: 0 16px;
color: #F7F8F8;
-webkit-font-smoothing: antialiased;
}
.tv-tool-card {
background: #111827;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 16px;
overflow: hidden;
}
.tv-tool-header {
padding: 28px 32px 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.tv-tool-title {
margin: 0 0 6px;
font-size: 24px;
font-weight: 600;
line-height: 1.2;
color: #F7F8F8;
letter-spacing: -0.01em;
}
.tv-tool-sub {
margin: 0;
font-size: 14px;
color: #8A8F9A;
line-height: 1.5;
}
.tv-tool-body {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
}
@media (max-width: 768px) {
.tv-tool-body {
grid-template-columns: 1fr;
}
}
.tv-tool-inputs {
padding: 24px 32px;
border-right: 1px solid rgba(255, 255, 255, 0.06);
}
@media (max-width: 768px) {
.tv-tool-inputs {
border-right: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
}
.tv-tool-outputs {
padding: 24px 32px;
background: rgba(0, 0, 0, 0.2);
}
/* Fields */
.tv-field {
margin-bottom: 16px;
}
.tv-field:last-child {
margin-bottom: 0;
}
.tv-field label {
display: block;
font-size: 12px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.04em;
color: #8A8F9A;
margin-bottom: 6px;
}
.tv-optional {
text-transform: none;
color: #6B7280;
font-weight: 400;
letter-spacing: 0;
}
.tv-field input[type="number"],
.tv-field select {
width: 100%;
background: #0A0A0A;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 10px 12px;
color: #F7F8F8;
font-size: 15px;
font-family: inherit;
font-variant-numeric: tabular-nums;
transition: border-color 0.15s ease;
-moz-appearance: textfield;
}
.tv-field input[type="number"]::-webkit-outer-spin-button,
.tv-field input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.tv-field input[type="number"]:focus,
.tv-field select:focus {
outline: none;
border-color: #00D4FF;
}
.tv-field select {
cursor: pointer;
appearance: none;
-webkit-appearance: none;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%238A8F9A' d='M6 8L0 0h12z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 14px center;
padding-right: 36px;
}
.tv-hint {
display: block;
font-size: 12px;
color: #00D4FF;
margin-top: 4px;
font-variant-numeric: tabular-nums;
}
/* Toggle buttons */
.tv-toggle {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
background: #0A0A0A;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 3px;
}
.tv-toggle-btn {
background: transparent;
border: none;
padding: 8px 12px;
color: #8A8F9A;
font-size: 14px;
font-weight: 500;
font-family: inherit;
cursor: pointer;
border-radius: 6px;
transition: all 0.15s ease;
}
.tv-toggle-btn:hover {
color: #F7F8F8;
}
.tv-toggle-active {
background: #00D4FF;
color: #0A0A0A;
}
.tv-toggle-active:hover {
color: #0A0A0A;
}
/* Checkbox */
.tv-field-checkbox {
padding-top: 4px;
}
.tv-checkbox-label {
display: flex !important;
align-items: center;
gap: 10px;
cursor: pointer;
text-transform: none !important;
letter-spacing: 0 !important;
font-size: 14px !important;
font-weight: 400 !important;
color: #F7F8F8 !important;
margin: 0 !important;
}
.tv-checkbox-label input[type="checkbox"] {
width: 18px;
height: 18px;
accent-color: #00D4FF;
cursor: pointer;
margin: 0;
}
.tv-prop-details {
margin-top: 8px;
padding: 16px;
background: rgba(124, 58, 237, 0.08);
border: 1px solid rgba(124, 58, 237, 0.25);
border-radius: 8px;
}
.tv-prop-details .tv-field:last-child {
margin-bottom: 0;
}
.tv-hidden {
display: none;
}
/* Outputs */
.tv-output-group {
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.tv-output-group:last-of-type {
border-bottom: none;
padding-bottom: 0;
}
.tv-output-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.tv-output-cell {
min-width: 0;
}
.tv-output-label {
font-size: 11px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #8A8F9A;
margin-bottom: 4px;
}
.tv-output-value {
font-size: 20px;
font-weight: 600;
color: #F7F8F8;
font-variant-numeric: tabular-nums;
letter-spacing: -0.01em;
}
.tv-output-primary {
font-size: 36px;
color: #00D4FF;
font-weight: 700;
line-height: 1.1;
}
.tv-output-sub {
font-size: 12px;
color: #8A8F9A;
margin-top: 4px;
font-variant-numeric: tabular-nums;
}
/* Warnings */
.tv-warnings {
margin-top: 20px;
display: flex;
flex-direction: column;
gap: 8px;
}
.tv-warning {
padding: 10px 12px;
border-radius: 6px;
font-size: 13px;
line-height: 1.4;
display: flex;
align-items: flex-start;
gap: 8px;
}
.tv-warning-icon {
flex-shrink: 0;
font-weight: 700;
font-size: 14px;
}
.tv-warning-amber {
background: rgba(255, 193, 7, 0.1);
border: 1px solid rgba(255, 193, 7, 0.3);
color: #FFC107;
}
.tv-warning-red {
background: rgba(255, 68, 102, 0.1);
border: 1px solid rgba(255, 68, 102, 0.3);
color: #FF4466;
}
.tv-warning-cyan {
background: rgba(0, 212, 255, 0.08);
border: 1px solid rgba(0, 212, 255, 0.25);
color: #00D4FF;
}
/* Footer */
.tv-tool-footer {
padding: 16px 32px 24px;
border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.tv-disclaimer {
margin: 0;
font-size: 12px;
color: #6B7280;
line-height: 1.5;
}
.tv-link {
color: #00D4FF;
text-decoration: none;
margin-left: 4px;
}
.tv-link:hover {
text-decoration: underline;
}
/* Mobile */
@media (max-width: 768px) {
.tv-tool-header,
.tv-tool-inputs,
.tv-tool-outputs,
.tv-tool-footer {
padding-left: 20px;
padding-right: 20px;
}
.tv-tool-title {
font-size: 20px;
}
.tv-output-primary {
font-size: 30px;
}
}
(function() {
'use strict';
// Instrument specs: [point value in $, tick size, display name]
var INSTRUMENTS = {
ES: { pointValue: 50, tickSize: 0.25, name: 'ES' },
MES: { pointValue: 5, tickSize: 0.25, name: 'MES' },
NQ: { pointValue: 20, tickSize: 0.25, name: 'NQ' },
MNQ: { pointValue: 2, tickSize: 0.25, name: 'MNQ' },
YM: { pointValue: 5, tickSize: 1.00, name: 'YM' },
MYM: { pointValue: 0.50, tickSize: 1.00, name: 'MYM' },
RTY: { pointValue: 50, tickSize: 0.10, name: 'RTY' },
M2K: { pointValue: 5, tickSize: 0.10, name: 'M2K' },
CL: { pointValue: 1000, tickSize: 0.01, name: 'CL' },
MCL: { pointValue: 100, tickSize: 0.01, name: 'MCL' },
NG: { pointValue: 10000, tickSize: 0.001, name: 'NG' },
GC: { pointValue: 100, tickSize: 0.10, name: 'GC' },
MGC: { pointValue: 10, tickSize: 0.10, name: 'MGC' },
SI: { pointValue: 5000, tickSize: 0.005, name: 'SI' },
CUSTOM: { pointValue: 50, tickSize: 0.01, name: 'Custom' }
};
var state = {
direction: 'long'
};
// Element refs
var $ = function(id) { return document.getElementById(id); };
var els = {
account: $('tv-account'),
riskPct: $('tv-risk-pct'),
riskDollarHint: $('tv-risk-dollar-hint'),
instrument: $('tv-instrument'),
customWrap: $('tv-custom-wrapper'),
customPv: $('tv-custom-pv'),
entry: $('tv-entry'),
stop: $('tv-stop'),
target: $('tv-target'),
propToggle: $('tv-propfirm-toggle'),
propDetails: $('tv-prop-details'),
dailyLoss: $('tv-daily-loss'),
maxDrawdown: $('tv-max-drawdown'),
outContracts: $('tv-out-contracts'),
outContractsNote: $('tv-out-contracts-note'),
outStopDist: $('tv-out-stop-distance'),
outActualRisk:$('tv-out-actual-risk'),
outRewardDist:$('tv-out-reward-distance'),
outProfit: $('tv-out-profit'),
outRR: $('tv-out-rr'),
warnings: $('tv-warnings'),
toggleBtns: document.querySelectorAll('.tv-toggle-btn')
};
function fmtCurrency(n) {
if (isNaN(n) || !isFinite(n)) return 'u2014';
var abs = Math.abs(n);
var sign = n = 1000) {
return sign + '$' + abs.toLocaleString('en-US', { maximumFractionDigits: 0 });
}
return sign + '$' + abs.toFixed(2);
}
function fmtPoints(n) {
if (isNaN(n) || !isFinite(n)) return 'u2014';
return n.toFixed(2) + ' pts';
}
function fmtRatio(rr) {
if (isNaN(rr) || !isFinite(rr) || rr 0) {
spec = { pointValue: cv, tickSize: 0.01, name: 'Custom' };
}
}
return spec;
}
function addWarning(type, msg) {
var div = document.createElement('div');
div.className = 'tv-warning tv-warning-' + type;
div.innerHTML = '
' +
(type === 'red' ? '!' : type === 'amber' ? '!' : 'i') +
'' + msg + '';
els.warnings.appendChild(div);
}
function calculate() {
// Clear warnings
els.warnings.innerHTML = '';
// Parse inputs
var account = parseFloat(els.account.value);
var riskPct = parseFloat(els.riskPct.value);
var entry = parseFloat(els.entry.value);
var stop = parseFloat(els.stop.value);
var target = parseFloat(els.target.value);
var instrument = getInstrument();
// Validation
if (isNaN(account) || account <= 0) {
resetOutputs();
return;
}
if (isNaN(riskPct) || riskPct <= 0) {
resetOutputs();
return;
}
// Compute dollar risk from %
var dollarRisk = account * (riskPct / 100);
els.riskDollarHint.textContent = '= ' + fmtCurrency(dollarRisk);
if (isNaN(entry) || isNaN(stop)) {
resetOutputs();
return;
}
// Stop distance
var stopDistance;
if (state.direction === 'long') {
stopDistance = entry - stop;
} else {
stopDistance = stop - entry;
}
if (stopDistance <= 0) {
addWarning('red', 'Stop loss is on the wrong side of entry for a ' +
state.direction + ' position. Check your numbers.');
resetOutputs(true);
return;
}
// Dollar risk per contract
var dollarPerContract = stopDistance * instrument.pointValue;
if (dollarPerContract 0) {
dollarProfit = rewardDistance * instrument.pointValue * contracts;
if (dollarPerContract > 0) {
rr = rewardDistance / stopDistance;
}
} else if (rewardDistance 0 ? contracts.toString() : '0';
if (contracts === 0) {
els.outContractsNote.textContent =
'Can't size even 1 contract with this risk. Increase risk % or widen stop.';
} else {
els.outContractsNote.textContent =
rawContracts > contracts ?
'(rounded down from ' + rawContracts.toFixed(2) + ')' :
'';
}
els.outStopDist.textContent =
fmtPoints(stopDistance) + ' / ' + Math.round(stopTicks) + ' ticks';
els.outActualRisk.textContent = fmtCurrency(actualRisk);
if (rewardDistance !== null && rewardDistance > 0) {
els.outRewardDist.textContent = fmtPoints(rewardDistance);
els.outProfit.textContent = fmtCurrency(dollarProfit);
els.outRR.textContent = fmtRatio(rr);
} else {
els.outRewardDist.textContent = 'u2014';
els.outProfit.textContent = 'u2014';
els.outRR.textContent = 'u2014';
}
// ========== WARNINGS ==========
// Stop too tight
if (stopTicks 2%
if (riskPct > 2) {
addWarning('amber',
'Risking ' + riskPct + '% per trade is aggressive. ' +
'Most professional traders keep risk under 2%.');
}
// R:R below 1
if (rr !== null && rr 0) {
addWarning('red',
'Your risk budget ($' + dollarRisk.toFixed(0) + ') is less than the cost of ' +
'1 contract ($' + dollarPerContract.toFixed(0) + '). ' +
'Consider a micro contract (MES, MNQ, MGC) or widen your risk.');
}
// Prop firm warnings
if (els.propToggle.checked) {
var dailyLoss = parseFloat(els.dailyLoss.value);
var maxDrawdown = parseFloat(els.maxDrawdown.value);
if (!isNaN(dailyLoss) && dailyLoss > 0 && actualRisk > dailyLoss) {
addWarning('red',
'This trade risks $' + actualRisk.toFixed(0) + ' which exceeds your ' +
'daily loss limit of $' + dailyLoss.toFixed(0) + '. ' +
'One stop-out ends your trading day.');
} else if (!isNaN(dailyLoss) && dailyLoss > 0 && actualRisk > dailyLoss * 0.5) {
addWarning('amber',
'This trade uses ' + Math.round((actualRisk / dailyLoss) * 100) + '% of your ' +
'daily loss limit on a single position. Very little margin for error.');
}
if (!isNaN(maxDrawdown) && maxDrawdown > 0 && actualRisk > maxDrawdown * 0.1) {
addWarning('amber',
'This trade risks ' + Math.round((actualRisk / maxDrawdown) * 100) + '% of your ' +
'max drawdown on a single position. Typical prop firm survival rules ' +
'suggest keeping single-trade risk under 10%.');
}
}
}
function resetOutputs(keepHint) {
els.outContracts.textContent = 'u2014';
els.outContractsNote.textContent = '';
els.outStopDist.textContent = 'u2014';
els.outActualRisk.textContent = 'u2014';
els.outRewardDist.textContent = 'u2014';
els.outProfit.textContent = 'u2014';
els.outRR.textContent = 'u2014';
}
// Event listeners
[els.account, els.riskPct, els.entry, els.stop, els.target,
els.customPv, els.dailyLoss, els.maxDrawdown].forEach(function(el) {
if (el) el.addEventListener('input', calculate);
});
els.instrument.addEventListener('change', function() {
if (els.instrument.value === 'CUSTOM') {
els.customWrap.classList.remove('tv-hidden');
} else {
els.customWrap.classList.add('tv-hidden');
}
calculate();
});
els.propToggle.addEventListener('change', function() {
if (els.propToggle.checked) {
els.propDetails.classList.remove('tv-hidden');
} else {
els.propDetails.classList.add('tv-hidden');
}
calculate();
});
els.toggleBtns.forEach(function(btn) {
btn.addEventListener('click', function() {
els.toggleBtns.forEach(function(b) { b.classList.remove('tv-toggle-active'); });
btn.classList.add('tv-toggle-active');
state.direction = btn.getAttribute('data-direction');
calculate();
});
});
// Initial calculation
calculate();
})();