minimalist-money-management-calculator
Version:
Set of utility functions for CFD money management with bunjs
98 lines (86 loc) • 3.9 kB
text/typescript
import { Strategy, Market, TradeInfo, Volume } from './types';
export class Risk {
private readonly CURRENCY_DECIMALS = 2;
constructor(private readonly strategy: Strategy) {}
/**
* Volume for one trade
* @param market Market
* @param tradeInfo TradeInfo
* @param floor boolean, default false, Volume are rounds to the neariest volume possible by your broker (Market.lotStep), pass true for the lowest
* @returns Volume,
* @example
* 20 stop points,
* 1000E balance,
* 1% de risk,
* Minimum volume,
*
* 1% of 1000 => 10$,
* 10/20 => 0.5$ per point,
* Value of a standart contract (≃ 375000 for DAX40) / DAX40 current price (15000) => 25 (lot in euros per point),
* 0.5 / 25 (lot in euros per point) => 0.02 lot
*/
public volume(market: Market, tradeInfo: TradeInfo, floor: boolean = false): Volume | Error {
if (!Number.isInteger(tradeInfo.stopDistance) || tradeInfo.stopDistance < 1) {
return new Error('StopDistance has to be integrer, minimum 1 !');
}
const riskMaxPerPos: number = this.strategy.initialBalance * (this.strategy.riskPerPos / 100); // In euros
const riskPerPoint: number = riskMaxPerPos / tradeInfo.stopDistance; // In euros
const currencyPerPipPerLot: number = market.unitPerLotStandard; // In euros for one full contract
const volumePerPipPerLotStep: number = currencyPerPipPerLot * market.lotStep;
const posVolume: number = riskPerPoint / currencyPerPipPerLot;
if ((volumePerPipPerLotStep * (posVolume / market.lotStep)).toFixed(this.CURRENCY_DECIMALS) === riskPerPoint.toFixed(this.CURRENCY_DECIMALS)) {
const forcedToMinimum = posVolume < market.lotStep;
const finalVol = floor ? parseFloat(Math.floor(posVolume).toFixed(this.CURRENCY_DECIMALS)) : parseFloat(posVolume.toFixed(this.CURRENCY_DECIMALS));
if (posVolume < market.lotStep) {
return {
lot: market.lotStep,
forcedToMinimum: forcedToMinimum
};
}
return {
lot: finalVol,
forcedToMinimum: forcedToMinimum
};
} else {
return new Error('Something went wrong when try to calcul volume');
}
}
// forexVolume(market: Market, tradeInfo: TradeInfo, floor: boolean = false, effectiveEntry, market, tradeInfo, investorConfig) {
// const inst = tradeInfo.trade.inst;
// const point = market.instInfo.tickSize * 10;
// const oneLotValue = market.instInfo.contractSize;
// const oneLotMargin = (market.instInfo.leverage * oneLotValue) / 100;
// valuePerPipPerLot = (
// (market.instInfo.contractSize / effectiveEntry) *
// point
// ).toFixed(EUROS_DECIMALS); // En euros pour un lot plein
// valuePerPipPerMicroLot = valuePerPipPerLot / 100,
// riskMaxPerPos =
// investorConfig.virtualBalance * (investorConfig.riskPerPos / 100); // En euros
// stopInitialSize = Math.abs(
// (tradeInfo.trade.stop - tradeInfo.trade.entry).toFixed(market.digits)
// ); // en Pips float
// const stopAbsolute = stopInitialSize / point; // en points
// const riskPerPoint = riskMaxPerPos / stopAbsolute; // en euros
// const posVolume = riskPerPoint / valuePerPipPerLot;
// if (
// (valuePerPipPerMicroLot * (posVolume / market.instInfo.lotStep)).toFixed(
// EUROS_DECIMALS
// ) === riskPerPoint.toFixed(EUROS_DECIMALS)
// ) {
// const finalVol = parseFloat(posVolume.toFixed(EUROS_DECIMALS));
// if (finalVol === 0) {
// console.log(`Volume de transation à 0, décalage vers un volume minimum`);
// return 0.01;
// }
// console.log(`Volume de transation :${finalVol}`);
// return parseFloat(finalVol);
// } else {
// // BUG Stopper le trade en cours, lorsque cette erreur est call
// throw new Error(
// `Le volume de trading n'est pas correct, sur ${inst}`,
// "risk => forexVolume"
// );
// }
// }
}