A message from our CEO about the future of Stack Overflow and Stack Exchange. Read now.

1 of 8

# This code just looks ugle… but it works… but there must be a better way though

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.

/// <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 524.16 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] [TestCategory("Trade Stop-Loss")] 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.