Currently the following formula is used to calculate the position of the level marker on the arc:
public static double GetLevelAngle(double level, int playerLevel) => (Constants.CPMultipliers[level] - 0.094) * 202.037116 / Constants.CPMultipliers[playerLevel];
This is incorrect, as it assumes that the dot is at the end of the arc when level == playerLevel. However, the dot will be at the end when level == (playerLevel + 1.5).
I believe the correct formula is:
public static double GetLevelAngle(double level, int playerLevel) => (Constants.CPMultipliers[level] - 0.094) * 180.0 / (Constants.CPMultipliers[min(playerLevel, 39.0) + 1.5] - 0.094);
I'm not sure if 180.0 is indeed exact (the arc doesn't look exactly like a perfect semicircle to me), or whether the min() is really needed around the playerLevel clamp (I have no screenshots from level 40 accounts to check - I'm assuming it's needed since the max allowed pokemon level is 40.5, which is already attainable at player level 30; levels 41 and 41.5 are unimplemented), but it certainly looks like a more accurate predictor of where the dot will appear.
A few extreme examples, all of which represent fully powered-up pokemon (Pl = player level, Po = pokemon level):
Pl 1.0, Po 2.5 => old formula: 212.0334805 (off the scale, extreme overshoot)
Pl 4.0, Po 5.5 => old formula: 167.5404837 (extreme undershoot)
Pl 31.0, Po 32.5 =>old formula: 178.7632002 (slight undershoot)
All of this come out to exactly 180 with the new formula.
A real-life, not-fully-powered case:
I'm level 31, and have a pokemon powered up to level 30. Go on Tap wrongly detects it as level 31.5 (slight overshoot), manually adjusting to where the circle most closely matches the dot yields level 31 (barely perceptible overshoot). Manually adjusting to level 30 shows significant undershoot.
Automatic detection: Pl 31.0, Po 31.5 => old formula: 177.1213036, new formula: 178.3467437
Manual adjustment: Pl 31.0, Po 31.0 => old formula: 176.2953506, new formula: 177.5150762
Correct level: Pl 31.0, Po 30.0 => old formula: 174.6332321, new formula: 175.8414582
Notice how close the old formula for level 31 is to the new formula for level 30 - just barely higher, consistent with the almost undetectable overshoot seen after manual adjustment.
From reading the code, it looks like the GetLevelAngle function is only used for drawing the feedback circle & dot; actually detecting the dot position is done separately. If you can, please let me know where that code is located, and what formula it's currently using.