Need support for Profit/Loss Calculation
Need support for Profit/Loss Calculation
12 Feb 2023, 12:39
Dear all,
maybe I missed something or just did a silly mistake but I have the following issue.
In an OpenAPI Console App I do a "Batch Optimization" of one of my strategies to find the most profitable symbols. This includes the following steps:
- Fetch the symbols and (last) spot to calculate the spread
- Fetch the market data for the last 12 months (this is stored in a MSSQL database and only the latest ones are requested from the server)
- Calculate the pip and tick size/value (see code snippets below)
- Run the optimization and use the values for the position sizing (see code snippets below)
Get symbol attributes and calculate pip/tick size (Symbols is a list with all required properties like SymbolId, Base/QuoteAssetId, Bid, Ask, Ticksize, ...):
private static void OnSymbolByIdRes(ProtoOASymbolByIdRes response)
{
foreach (var symbol in response.Symbol)
{
// filter symbol that needs to be updated
var symbolToUpdate = Symbols!.FirstOrDefault(s => s.SymbolId == symbol.SymbolId);
// calculate tick size and pip size
symbolToUpdate!.TickSize = 1 / Math.Pow(10, symbol.Digits);
symbolToUpdate.PipSize = 1 / Math.Pow(10, symbol.PipPosition);
symbolToUpdate.Digits = symbol.Digits;
symbolToUpdate.PipPosition = symbol.PipPosition;
// send symbols for conversion request
Messages.SendSymbolsForConversionReq(response.CtidTraderAccountId, symbolToUpdate.QuoteAssetId, _depositAssetId);
}
}
Calculate conversion rate and pip/tick value by using the conversion chain:
private static void OnSymbolsForConversionRes(ProtoOASymbolsForConversionRes response)
{
// calculate conversion rate
var rate = 1.0;
var assetId = response.Symbol.FirstOrDefault()!.QuoteAssetId;
foreach (var symbol in response.Symbol)
{
// filter chain symbol from the database
var chainSymbol = Symbols!.FirstOrDefault(s => s.SymbolId == symbol.SymbolId);
// calculate conversion rate for each chain symbol
if (symbol.BaseAssetId == assetId)
{
rate *= chainSymbol!.LastBid;
assetId = symbol.QuoteAssetId;
}
else
{
rate /= chainSymbol!.LastBid;
assetId = symbol.BaseAssetId;
}
}
// filter all symbols with the same quote asset id from the database (redundancy!)
var symbolsToUpdate = Symbols!
.Where(s => s.QuoteAssetId == response.Symbol.FirstOrDefault()!.QuoteAssetId)
.Select(s => s);
foreach (var symbolToUpdate in symbolsToUpdate)
{
symbolToUpdate.TickValue = symbolToUpdate.TickSize * rate;
symbolToUpdate.PipValue = symbolToUpdate.TickValue * (symbolToUpdate.PipSize / symbolToUpdate.TickSize);
}
}
Use this information for calculating the position size:
var tradeSize = Math.Round(((Risk * balance) / (symbol.PipValue / symbol.PipSize)) / (StopLoss * symbol.PipSize), 2);
Calculating the profit of the closed position (trade is a class to "simulate" the open and close of positions):
var positionReturn = trade.ClosePrice - trade.OpenPrice;
var pipsReturn = Math.Round(positionReturn * Math.Pow(10, symbol.PipPosition),
symbol.Digits - symbol.PipPosition);
var profit = pipsReturn * symbol.PipValue * trade.TradeSize;
For the tradesize calculation I followed a forum entry and for the other parts I used the information as mentioned in Profit/Loss Calculation.
The strange thing is that this is working as expected for most of the symbols but calculates wrong results for symbols like USDTRY, EURNOK, USDDKK, EURPLN (to just list a few examples). So may be for these symbols something "special" is missing for what I didn't find any detailed information...
Many thanks in advance for your help.
Best regards,
Christian