```I have to project the price for a publicly traded contract when the price should be approximately matching the targeted profit or loss. I have written the following code, runs under 0.02ms.. could be a faster and could be a bit more elegant as the code will get slower progressively depending on the iterations needed. I have documented the code, hope that it makes sense.

class Contract
{
public string Symbol { get; set; }
public string Currency { get; set; }
public decimal CommisionPerTrade { get; set; }
public decimal MinTick { get; set; }
public string PriceFormat { get; set; }

private (int pos, decimal avgCost) position;

public void AssignPossition(int shares, decimal averagePrice)
{
position = (shares, averagePrice);
}

public (int pos, decimal avgCost) Position => position;

/// <summary>
/// Get an exchange acceptable price for a contract for a given Profit & Loss value.
/// The price depends on the rounding of a given contract, possible minimum - ticks are 100, 10, 1.0, 0.1, 0.01, 0.001, 0.0005
/// </summary>
/// <param name="pnl">The P&L that is aimed for.</param>
/// <param name="mayBeHigher">if set to <c>true</c> then loss may be higher as well as profit to take the next acceptable
/// contract price else accept less rather them more.</param>
/// <returns>a acceptable price at a given Min-Tick range that would get a approximate P&L</returns>
/// <remarks>Please note that loss is a negative number, loss may be higher is actually a lower number as a loss of -100 is
/// higher than a loss of -50</remarks>
public bool TryGetPriceAtPnl(decimal pnl, bool mayBeHigher, out double price)
{

if ((Math.Abs(pnl) <= CommisionPerTrade && mayBeHigher == false)

|| (Position.avgCost == 0 || Position.pos == 0))
{
price = 0;
return false;
}

//the current price paid for the contract
decimal priceAtPnl = Position.avgCost;

//get the Price for minimal price change (min-tick) in the current position on a contract
//positions when short contain negative values so best to take abs values
decimal pnlPerTick = (Math.Abs(Position.pos) * (Position.avgCost + MinTick))
- (Math.Abs(Position.pos) * Position.avgCost);

if (position.pos > 0)// long trade
{

if (pnl > 0) //profit target
{
//positive P&L
while ((!mayBeHigher && sum + pnlPerTick <= pnl) || (mayBeHigher && sum <= pnl))
{
sum += pnlPerTick;
priceAtPnl += MinTick;//next allowed price by adding the minimum price change
}
}
else //loss target
{
//when a negative P&L "may be higher" then this actually is, and must be, a lower value
while ((!mayBeHigher && sum - pnlPerTick >= pnl) || (mayBeHigher && sum >= pnl))
{
sum -= pnlPerTick;
priceAtPnl -= MinTick;//previous allowed price by subtracting the minimum price change
}
}
}
{
if (pnl > 0) //profit target
{
//positive P&L
while ((!mayBeHigher && sum + pnlPerTick >= pnl) || (mayBeHigher && sum >= pnl))
{
sum += pnlPerTick;
priceAtPnl -= MinTick;//next allowed price by adding the minimum price change
}
}
else //loss target
{
//when a negative P&L "may be higher" then this actually is, and must be, a lower value
while ((!mayBeHigher && sum - pnlPerTick >= pnl) || (mayBeHigher && sum >= pnl))
{
sum -= pnlPerTick;
priceAtPnl += MinTick;//previous allowed price by subtracting the minimum price change
}
}

}

//can't have floating point imprecision but need to return a double so cast the decimal to a double
price = Convert.ToDouble(priceAtPnl);
return true;
}

}

I have several unit tests and all look to be projecting the correct price for both long and short trades as well as profit targets and stop-loss prices.

Here are 2 unit tests

public void NativeTrainerTestPriceAtPnlGoodCaseShortLoss()
{

var contract = new Contract() { Symbol = "ABC", Currency = "ABC", CommisionPerTrade = 238.50M + 238.48M, MinTick = 0.001M, PriceFormat = "N2" };
contract.AssignPossition(shares: -100_000, averagePrice: 118.891M);

/* P&L table for short trades
* ______________________________________
* PRICE       P&L       Min-Ticks
* 118.901    -1476.98    10           P
* 118.900    -1376.98    9            R
* 118.899    -1276.98    8            I
* 118.898    -1176.98    7            C
* 118.897    -1076.98    6            E
* 118.896     -976.98    5
* 118.895     -876.98    4            U
* 118.894     -776.98    3            P
* 118.893     -676.98    2
* 118.892     -576.98    1
* 118.891     -476.98    0  ---      BASE PRICE
* 118.890     -376.98    1
* 118.889     -276.98    2           P
* 118.888     -276.98    3           R
* 118.887     -176.98    4           I
* 118.886      -76.98    5           C
* 118.885       23.02    6           E
* 118.884      123.02    7
* 118.883      223.02    8           D
* 118.882      323.02    9           O
* 118.881      423.02    10          W
* 118.880      523.02    11          N
*/

//warm up .net to make sure we are jitted
contract.TryGetPriceAtPnl(700, false, out double _);

var sw = System.Diagnostics.Stopwatch.StartNew();
Assert.IsTrue(contract.TryGetPriceAtPnl(pnl: -700.00M, mayBeHigher: false, out double price), "Calculate price for P&L 700 must be possible, however returned false");
sw.Stop();

Assert.IsTrue(price > 0, "Price must always be more then 0");
Assert.AreEqual(118.893D, price);
if (sw.ElapsedMilliseconds > 0.02)
Assert.Inconclusive(\$"Non Functional requirement, price calculations must be under 2 ms, this server did it in {sw.Elapsed}");

}
[TestMethod]
public void NativeTrainerTestPriceAtPnlGoodCaseLongLoss()
{

/* P&L table for long trades
* ______________________________________
* PRICE       P&L       Min-Ticks
* 118.936      523.12   10
* 118.935      423.12    9
* 118.934      323.12    8
* 118.933      223.12    7
* 118.932      123.12    6
* 118.931       23.12    5
* 118.930     - 76.88    4
* 118.929     -176.88    3
* 118.928     -276.88    2
* 118.927     -376.88    1
* 118.926     -476.88    0  ---
* 118.925     -576.88    1
* 118.924     -676.88    2
* 118.923     -767.88    3
* 118.922     -867.88    4
* 118.921     -967.88    5

*/

var contract = new Contract() { Symbol = "ABC", Currency = "ABC", CommisionPerTrade = 238.43M + 238.45M, MinTick = 0.001M, PriceFormat = "N2" };
contract.AssignPossition(shares: 100_000, averagePrice: 118.926M);

//warm up .net to make sure we are jitted
contract.TryGetPriceAtPnl(700, false, out double _);

var sw = System.Diagnostics.Stopwatch.StartNew();
contract.TryGetPriceAtPnl(pnl: -700M, mayBeHigher: false, out double price);
sw.Stop();
Assert.AreEqual(118.924D, price);

contract.TryGetPriceAtPnl(pnl: -700M, mayBeHigher: true, out price);
Assert.AreEqual(118.923D, price);

if (sw.ElapsedMilliseconds > 0.02)
Assert.Inconclusive(\$"Non Functional requirement, price calculations must be under 2 ms, this server did it in {sw.Elapsed}");

}

Ideally I would like to project the price without the loops. Important is that one must project valid prices as else the exchange will reject the order.

```