diff options
Diffstat (limited to 'portfolio.py')
-rw-r--r-- | portfolio.py | 98 |
1 files changed, 57 insertions, 41 deletions
diff --git a/portfolio.py b/portfolio.py index efd9b84..b629966 100644 --- a/portfolio.py +++ b/portfolio.py | |||
@@ -149,7 +149,7 @@ class Amount: | |||
149 | 149 | ||
150 | def __floordiv__(self, value): | 150 | def __floordiv__(self, value): |
151 | if not isinstance(value, (int, float, D)): | 151 | if not isinstance(value, (int, float, D)): |
152 | raise TypeError("Amount may only be multiplied by integers") | 152 | raise TypeError("Amount may only be divided by numbers") |
153 | return Amount(self.currency, self.value / value) | 153 | return Amount(self.currency, self.value / value) |
154 | 154 | ||
155 | def __truediv__(self, value): | 155 | def __truediv__(self, value): |
@@ -290,11 +290,10 @@ class Trade: | |||
290 | else: | 290 | else: |
291 | return "long" | 291 | return "long" |
292 | 292 | ||
293 | @property | 293 | def filled_amount(self, in_base_currency=False): |
294 | def filled_amount(self): | ||
295 | filled_amount = 0 | 294 | filled_amount = 0 |
296 | for order in self.orders: | 295 | for order in self.orders: |
297 | filled_amount += order.filled_amount | 296 | filled_amount += order.filled_amount(in_base_currency=in_base_currency) |
298 | return filled_amount | 297 | return filled_amount |
299 | 298 | ||
300 | def update_order(self, order, tick): | 299 | def update_order(self, order, tick): |
@@ -329,54 +328,69 @@ class Trade: | |||
329 | if inverted: | 328 | if inverted: |
330 | ticker = ticker["original"] | 329 | ticker = ticker["original"] |
331 | rate = Computation.compute_value(ticker, self.order_action(inverted), compute_value=compute_value) | 330 | rate = Computation.compute_value(ticker, self.order_action(inverted), compute_value=compute_value) |
332 | # 0.1 | ||
333 | 331 | ||
334 | delta_in_base = abs(self.value_from - self.value_to) | 332 | delta_in_base = abs(self.value_from - self.value_to) |
335 | # 9 BTC's worth of move (10 - 1 or 1 - 10 depending on case) | 333 | # 9 BTC's worth of move (10 - 1 or 1 - 10 depending on case) |
336 | 334 | ||
337 | if not inverted: | 335 | if not inverted: |
338 | currency = self.base_currency | 336 | base_currency = self.base_currency |
339 | # BTC | 337 | # BTC |
340 | if self.action == "dispose": | 338 | if self.action == "dispose": |
341 | # I have 10 BTC worth of FOO, and I want to sell 9 BTC worth of it | 339 | filled = self.filled_amount(in_base_currency=False) |
342 | # At rate 1 Foo = 0.1 BTC | 340 | delta = delta_in_base.in_currency(self.currency, self.market, rate=1/self.value_from.rate) |
343 | value_from = self.value_from.linked_to | 341 | # I have 10 BTC worth of FOO, and I want to sell 9 BTC |
344 | # value_from = 100 FOO | 342 | # worth of it, computed first with rate 10 FOO = 1 BTC. |
345 | value_to = self.value_to.in_currency(self.currency, self.market, rate=1/self.value_from.rate) | 343 | # -> I "sell" "90" FOO at proposed rate "rate". |
346 | # value_to = 10 FOO (1 BTC * 1/0.1) | 344 | |
347 | delta = abs(value_to - value_from) | 345 | delta = delta - filled |
348 | # delta = 90 FOO | 346 | # I already sold 60 FOO, 30 left |
349 | # Action: "sell" "90 FOO" at rate "0.1" "BTC" on "market" | ||
350 | |||
351 | # Note: no rounding error possible: if we have value_to == 0, then delta == value_from | ||
352 | else: | 347 | else: |
353 | delta = delta_in_base.in_currency(self.currency, self.market, rate=1/rate) | 348 | filled = self.filled_amount(in_base_currency=True) |
354 | # I want to buy 9 / 0.1 FOO | 349 | delta = (delta_in_base - filled).in_currency(self.currency, self.market, rate=1/rate) |
355 | # Action: "buy" "90 FOO" at rate "0.1" "BTC" on "market" | 350 | # I want to buy 9 BTC worth of FOO, computed with rate |
351 | # 10 FOO = 1 BTC | ||
352 | # -> I "buy" "9 / rate" FOO at proposed rate "rate" | ||
353 | |||
354 | # I already bought 3 / rate FOO, 6 / rate left | ||
356 | else: | 355 | else: |
357 | currency = self.currency | 356 | base_currency = self.currency |
358 | # FOO | 357 | # FOO |
359 | delta = delta_in_base | 358 | if self.action == "dispose": |
360 | # sell: | 359 | filled = self.filled_amount(in_base_currency=True) |
361 | # I have 10 BTC worth of FOO, and I want to sell 9 BTC worth of it | 360 | # Base is FOO |
362 | # At rate 1 Foo = 0.1 BTC | 361 | |
363 | # Action: "buy" "9 BTC" at rate "1/0.1" "FOO" on market | 362 | delta = (delta_in_base.in_currency(self.currency, self.market, rate=1/self.value_from.rate) |
364 | # buy: | 363 | - filled).in_currency(self.base_currency, self.market, rate=1/rate) |
365 | # I want to buy 9 / 0.1 FOO | 364 | # I have 10 BTC worth of FOO, and I want to sell 9 BTC worth of it |
366 | # Action: "sell" "9 BTC" at rate "1/0.1" "FOO" on "market" | 365 | # computed at rate 1 Foo = 0.01 BTC |
367 | if self.value_to == 0: | 366 | # Computation says I should sell it at 125 FOO / BTC |
368 | rate = self.value_from.linked_to.value / self.value_from.value | 367 | # -> delta_in_base = 9 BTC |
369 | # Recompute the rate to avoid any rounding error | 368 | # -> delta = (9 * 1/0.01 FOO) * 1/125 = 7.2 BTC |
369 | # Action: "buy" "7.2 BTC" at rate "125" "FOO" on market | ||
370 | |||
371 | # I already bought 300/125 BTC, only 600/125 left | ||
372 | else: | ||
373 | filled = self.filled_amount(in_base_currency=False) | ||
374 | # Base is FOO | ||
375 | |||
376 | delta = delta_in_base | ||
377 | # I have 1 BTC worth of FOO, and I want to buy 9 BTC worth of it | ||
378 | # At rate 100 Foo / BTC | ||
379 | # Computation says I should buy it at 125 FOO / BTC | ||
380 | # -> delta_in_base = 9 BTC | ||
381 | # Action: "sell" "9 BTC" at rate "125" "FOO" on market | ||
382 | |||
383 | delta = delta - filled | ||
384 | # I already sold 4 BTC, only 5 left | ||
370 | 385 | ||
371 | close_if_possible = (self.value_to == 0) | 386 | close_if_possible = (self.value_to == 0) |
372 | 387 | ||
373 | if delta <= self.filled_amount: | 388 | if delta <= 0: |
374 | print("Less to do than already filled: {} <= {}".format(delta, | 389 | print("Less to do than already filled: {}".format(delta)) |
375 | self.filled_amount)) | ||
376 | return | 390 | return |
377 | 391 | ||
378 | self.orders.append(Order(self.order_action(inverted), | 392 | self.orders.append(Order(self.order_action(inverted), |
379 | delta - self.filled_amount, rate, currency, self.trade_type, | 393 | delta, rate, base_currency, self.trade_type, |
380 | self.market, self, close_if_possible=close_if_possible)) | 394 | self.market, self, close_if_possible=close_if_possible)) |
381 | 395 | ||
382 | def __repr__(self): | 396 | def __repr__(self): |
@@ -497,15 +511,17 @@ class Order: | |||
497 | def remaining_amount(self): | 511 | def remaining_amount(self): |
498 | if self.status == "open": | 512 | if self.status == "open": |
499 | self.fetch() | 513 | self.fetch() |
500 | return self.amount - self.filled_amount | 514 | return self.amount - self.filled_amount() |
501 | 515 | ||
502 | @property | 516 | def filled_amount(self, in_base_currency=False): |
503 | def filled_amount(self): | ||
504 | if self.status == "open": | 517 | if self.status == "open": |
505 | self.fetch() | 518 | self.fetch() |
506 | filled_amount = Amount(self.amount.currency, 0) | 519 | filled_amount = 0 |
507 | for mouvement in self.mouvements: | 520 | for mouvement in self.mouvements: |
508 | filled_amount += mouvement.total | 521 | if in_base_currency: |
522 | filled_amount += mouvement.total_in_base | ||
523 | else: | ||
524 | filled_amount += mouvement.total | ||
509 | return filled_amount | 525 | return filled_amount |
510 | 526 | ||
511 | def fetch_mouvements(self): | 527 | def fetch_mouvements(self): |