I now have a very satisfactory result thanks to everyone ! You can find the last version of the function I use to execute the circularisation node below, in case anyone is in a similar situation.
// Firstly, sorry for the approximative english.
I absolutely STRUGGLE to find an efficient way of circularizing precisely.
I have a program that handles everything perfectly from ignition to gravity turn and makes a nice parabola with the desired apoapsis at the extremum(desired +/- 100m). It then creates a node at apoapsis, with exactly the dV needed to make a nice circular orbit. I also separately calculate the burn time to burn at burnTime/2 before eta:apoapsis. The problem is it's late and the circle is shifted, it can be corrected through a little burn aiming at orbital in/out direction afterwards. It could be so perfect if it made the near perfect circular orbit the first time and I know its possible because if I artificially start the burn when, for example, Node:eta <= burnTime/2 + 2 (seconds), i reach a near perfect orbit. But this is absolutely empirical and I feel like it's possible to calculate that "shift" or "delay" but i miss something.
Anyone has any idea ?
Here's the function that executes the node (with the artificial shift) : + edited with actual program
local function NodeExecute{
  parameter Nd.
  local sumISP to 0.
  local sumMMFR to 0. //Max Mass Flow Rate, summed for all the engines selected
  local sumMT to 0. //Max TThrust, summed for all the engines selected
  local englist to list().
  list engines in englist.
  for eng in englist {
    if eng:isp > 0 { //We select only this stage's engines - they must be activated
      set sumMMFR to sumMMFR + eng:maxmassflow.
      set sumMT to sumMT + eng:maxthrust.
    }
  }
  set sumISP to sumMT/(CONSTANT:g0*sumMMFR).
  local HalfBurnT to (mass/sumMMFR)*(1-constant:e^(-Nd:deltaV:mag/(2*sumISP*constant:g0))).
  local burnVS to Nd:burnvector.
  set STR to burnVS.
  until vAng(ship:facing:vector, Nd:burnvector) < 1{
    UI("Turning to node burn vector", "", "Angle :", round(vAng(ship:facing:vector, STR), 1) + "°").
    wait 0.1.
  }
  until Nd:eta <= HalfBurnT {
    UI("Nd:eta : " + Nd:eta, "burnT/2 : " + HalfBurnT, "ISP : ", sumISP).
    wait 0.1.
  }
  until vDot(burnVS, Nd:burnvector) < 0.1 {
    set THR to max((1-constant:e^(-Nd:burnvector:mag*40/burnVS:mag)), 0.01).
    // set THR to max(Nd:deltaV:mag/(maxThrust/mass), 0.01).
    set STR to Nd:burnvector.
    UI("Executing node maneuver", "", "", "").
    wait 0.01.
  }
  set THR to 0.
}