El uso de Andy Flury respuesta y poco pulido se da la siguiente clase de Python para la PnL de la calculadora:
class PnLCalculator:
def __init__(self):
self.quantity = 0
self.cost = 0.0
self.market_value = 0.0
self.r_pnl = 0.0
self.average_price = 0.0
def fill(self, n_pos, exec_price):
pos_change = n_pos - self.quantity
direction = np.sign(pos_change)
prev_direction = np.sign(self.quantity)
qty_closing = min(abs(self.quantity), abs(pos_change)) * direction if prev_direction != direction else 0
qty_opening = pos_change if prev_direction == direction else pos_change - qty_closing
new_cost = self.cost + qty_opening * exec_price
if self.quantity != 0:
new_cost += qty_closing * self.cost / self.quantity
self.r_pnl += qty_closing * (self.cost / self.quantity - exec_price)
self.quantity = n_pos
self.cost = new_cost
def update(self, price):
if self.quantity != 0:
self.average_price = self.cost / self.quantity
else:
self.average_price = 0
self.market_value = self.quantity * price
return self.market_value - self.cost
y el uso de ella :
positions = np.array([200, 100, -100, 150, 50, 0])
exec_prices = np.array([50.0, 51.0, 49.0, 51.0, 53.0, 52.0])
pnls = []
print('Pos\t|\tR.P&L\t|\tU P&L\t|\tAvgPrc')
print('-' * 55)
pos = PnLCalculator()
pnls = []
for (p,e) in zip(positions, exec_prices):
pos.fill(p, e)
u_pnl = pos.update(e)
print('%+d\t|\t%.1f\t|\t%.1f\t|\t[%.1f]' % (pos.quantity, pos.r_pnl, u_pnl, pos.average_price))
pnls.append(u_pnl + pos.r_pnl)
print('-' * 55)
print(pnls)
Salida:
Pos | R.P&L | U P&L | AvgPrc
-------------------------------------------------------
+200 | 0.0 | 0.0 | [50.0]
+100 | 100.0 | 100.0 | [50.0]
-100 | 0.0 | 0.0 | [49.0]
+150 | -200.0 | 0.0 | [51.0]
+50 | 0.0 | 100.0 | [51.0]
+0 | 50.0 | 0.0 | [0.0]
-------------------------------------------------------
[0.0, 200.0, 0.0, -200.0, 100.0, 50.0]