aboutsummaryrefslogtreecommitdiff
path: root/portfolio.py
diff options
context:
space:
mode:
Diffstat (limited to 'portfolio.py')
-rw-r--r--portfolio.py81
1 files changed, 38 insertions, 43 deletions
diff --git a/portfolio.py b/portfolio.py
index f9423b9..43a39c4 100644
--- a/portfolio.py
+++ b/portfolio.py
@@ -1,13 +1,10 @@
1import time 1import time
2from datetime import datetime, timedelta 2from datetime import datetime, timedelta
3from decimal import Decimal as D, ROUND_DOWN 3from decimal import Decimal as D, ROUND_DOWN
4# Put your poloniex api key in market.py
5from json import JSONDecodeError 4from json import JSONDecodeError
6from simplejson.errors import JSONDecodeError as SimpleJSONDecodeError 5from simplejson.errors import JSONDecodeError as SimpleJSONDecodeError
7from ccxt import ExchangeError, ExchangeNotAvailable 6from ccxt import ExchangeError, ExchangeNotAvailable
8import requests 7import requests
9import helper as h
10from store import *
11 8
12# FIXME: correctly handle web call timeouts 9# FIXME: correctly handle web call timeouts
13 10
@@ -18,26 +15,27 @@ class Portfolio:
18 last_date = None 15 last_date = None
19 16
20 @classmethod 17 @classmethod
21 def wait_for_recent(cls, delta=4): 18 def wait_for_recent(cls, market, delta=4):
22 cls.repartition(refetch=True) 19 cls.repartition(market, refetch=True)
23 while cls.last_date is None or datetime.now() - cls.last_date > timedelta(delta): 20 while cls.last_date is None or datetime.now() - cls.last_date > timedelta(delta):
24 time.sleep(30) 21 time.sleep(30)
25 cls.repartition(refetch=True) 22 market.report.print_log("Attempt to fetch up-to-date cryptoportfolio")
23 cls.repartition(market, refetch=True)
26 24
27 @classmethod 25 @classmethod
28 def repartition(cls, liquidity="medium", refetch=False): 26 def repartition(cls, market, liquidity="medium", refetch=False):
29 cls.parse_cryptoportfolio(refetch=refetch) 27 cls.parse_cryptoportfolio(market, refetch=refetch)
30 liquidities = cls.liquidities[liquidity] 28 liquidities = cls.liquidities[liquidity]
31 return liquidities[cls.last_date] 29 return liquidities[cls.last_date]
32 30
33 @classmethod 31 @classmethod
34 def get_cryptoportfolio(cls): 32 def get_cryptoportfolio(cls, market):
35 try: 33 try:
36 r = requests.get(cls.URL) 34 r = requests.get(cls.URL)
37 ReportStore.log_http_request(r.request.method, 35 market.report.log_http_request(r.request.method,
38 r.request.url, r.request.body, r.request.headers, r) 36 r.request.url, r.request.body, r.request.headers, r)
39 except Exception as e: 37 except Exception as e:
40 ReportStore.log_error("get_cryptoportfolio", exception=e) 38 market.report.log_error("get_cryptoportfolio", exception=e)
41 return 39 return
42 try: 40 try:
43 cls.data = r.json(parse_int=D, parse_float=D) 41 cls.data = r.json(parse_int=D, parse_float=D)
@@ -45,9 +43,9 @@ class Portfolio:
45 cls.data = None 43 cls.data = None
46 44
47 @classmethod 45 @classmethod
48 def parse_cryptoportfolio(cls, refetch=False): 46 def parse_cryptoportfolio(cls, market, refetch=False):
49 if refetch or cls.data is None: 47 if refetch or cls.data is None:
50 cls.get_cryptoportfolio() 48 cls.get_cryptoportfolio(market)
51 49
52 def filter_weights(weight_hash): 50 def filter_weights(weight_hash):
53 if weight_hash[1][0] == 0: 51 if weight_hash[1][0] == 0:
@@ -118,7 +116,7 @@ class Amount:
118 self.value * rate, 116 self.value * rate,
119 linked_to=self, 117 linked_to=self,
120 rate=rate) 118 rate=rate)
121 asset_ticker = h.get_ticker(self.currency, other_currency, market) 119 asset_ticker = market.get_ticker(self.currency, other_currency)
122 if asset_ticker is not None: 120 if asset_ticker is not None:
123 rate = Computation.compute_value(asset_ticker, action, compute_value=compute_value) 121 rate = Computation.compute_value(asset_ticker, action, compute_value=compute_value)
124 return Amount( 122 return Amount(
@@ -283,7 +281,7 @@ class Balance:
283 return "Balance({}".format(self.currency) + "".join([exchange, margin, total]) + ")" 281 return "Balance({}".format(self.currency) + "".join([exchange, margin, total]) + ")"
284 282
285class Trade: 283class Trade:
286 def __init__(self, value_from, value_to, currency, market=None): 284 def __init__(self, value_from, value_to, currency, market):
287 # We have value_from of currency, and want to finish with value_to of 285 # We have value_from of currency, and want to finish with value_to of
288 # that currency. value_* may not be in currency's terms 286 # that currency. value_* may not be in currency's terms
289 self.currency = currency 287 self.currency = currency
@@ -353,18 +351,18 @@ class Trade:
353 if tick == 7: 351 if tick == 7:
354 update = "market_fallback" 352 update = "market_fallback"
355 353
356 ReportStore.log_order(order, tick, update=update, 354 self.market.report.log_order(order, tick, update=update,
357 compute_value=compute_value, new_order=new_order) 355 compute_value=compute_value, new_order=new_order)
358 356
359 if new_order is not None: 357 if new_order is not None:
360 order.cancel() 358 order.cancel()
361 new_order.run() 359 new_order.run()
362 ReportStore.log_order(order, tick, new_order=new_order) 360 self.market.report.log_order(order, tick, new_order=new_order)
363 361
364 def prepare_order(self, compute_value="default"): 362 def prepare_order(self, compute_value="default"):
365 if self.action is None: 363 if self.action is None:
366 return None 364 return None
367 ticker = h.get_ticker(self.currency, self.base_currency, self.market) 365 ticker = self.market.get_ticker(self.currency, self.base_currency)
368 inverted = ticker["inverted"] 366 inverted = ticker["inverted"]
369 if inverted: 367 if inverted:
370 ticker = ticker["original"] 368 ticker = ticker["original"]
@@ -430,7 +428,7 @@ class Trade:
430 close_if_possible = (self.value_to == 0) 428 close_if_possible = (self.value_to == 0)
431 429
432 if delta <= 0: 430 if delta <= 0:
433 ReportStore.log_error("prepare_order", message="Less to do than already filled: {}".format(delta)) 431 self.market.report.log_error("prepare_order", message="Less to do than already filled: {}".format(delta))
434 return None 432 return None
435 433
436 order = Order(self.order_action(inverted), 434 order = Order(self.order_action(inverted),
@@ -456,11 +454,11 @@ class Trade:
456 self.action) 454 self.action)
457 455
458 def print_with_order(self, ind=""): 456 def print_with_order(self, ind=""):
459 ReportStore.print_log("{}{}".format(ind, self)) 457 self.market.report.print_log("{}{}".format(ind, self))
460 for order in self.orders: 458 for order in self.orders:
461 ReportStore.print_log("{}\t{}".format(ind, order)) 459 self.market.report.print_log("{}\t{}".format(ind, order))
462 for mouvement in order.mouvements: 460 for mouvement in order.mouvements:
463 ReportStore.print_log("{}\t\t{}".format(ind, mouvement)) 461 self.market.report.print_log("{}\t\t{}".format(ind, mouvement))
464 462
465class Order: 463class Order:
466 def __init__(self, action, amount, rate, base_currency, trade_type, market, 464 def __init__(self, action, amount, rate, base_currency, trade_type, market,
@@ -525,15 +523,15 @@ class Order:
525 523
526 def run(self): 524 def run(self):
527 symbol = "{}/{}".format(self.amount.currency, self.base_currency) 525 symbol = "{}/{}".format(self.amount.currency, self.base_currency)
528 amount = round(self.amount, self.market.order_precision(symbol)).value 526 amount = round(self.amount, self.market.ccxt.order_precision(symbol)).value
529 527
530 if TradeStore.debug: 528 if self.market.debug:
531 ReportStore.log_debug_action("market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format( 529 self.market.report.log_debug_action("market.ccxt.create_order('{}', 'limit', '{}', {}, price={}, account={})".format(
532 symbol, self.action, amount, self.rate, self.account)) 530 symbol, self.action, amount, self.rate, self.account))
533 self.results.append({"debug": True, "id": -1}) 531 self.results.append({"debug": True, "id": -1})
534 else: 532 else:
535 try: 533 try:
536 self.results.append(self.market.create_order(symbol, 'limit', self.action, amount, price=self.rate, account=self.account)) 534 self.results.append(self.market.ccxt.create_order(symbol, 'limit', self.action, amount, price=self.rate, account=self.account))
537 except ExchangeNotAvailable: 535 except ExchangeNotAvailable:
538 # Impossible to honor the order (dust amount) 536 # Impossible to honor the order (dust amount)
539 self.status = "closed" 537 self.status = "closed"
@@ -541,15 +539,15 @@ class Order:
541 return 539 return
542 except Exception as e: 540 except Exception as e:
543 self.status = "error" 541 self.status = "error"
544 action = "market.create_order('{}', 'limit', '{}', {}, price={}, account={})".format(symbol, self.action, amount, self.rate, self.account) 542 action = "market.ccxt.create_order('{}', 'limit', '{}', {}, price={}, account={})".format(symbol, self.action, amount, self.rate, self.account)
545 ReportStore.log_error(action, exception=e) 543 self.market.report.log_error(action, exception=e)
546 return 544 return
547 self.id = self.results[0]["id"] 545 self.id = self.results[0]["id"]
548 self.status = "open" 546 self.status = "open"
549 547
550 def get_status(self): 548 def get_status(self):
551 if TradeStore.debug: 549 if self.market.debug:
552 ReportStore.log_debug_action("Getting {} status".format(self)) 550 self.market.report.log_debug_action("Getting {} status".format(self))
553 return self.status 551 return self.status
554 # other states are "closed" and "canceled" 552 # other states are "closed" and "canceled"
555 if not self.finished: 553 if not self.finished:
@@ -559,23 +557,23 @@ class Order:
559 return self.status 557 return self.status
560 558
561 def mark_finished_order(self): 559 def mark_finished_order(self):
562 if TradeStore.debug: 560 if self.market.debug:
563 ReportStore.log_debug_action("Mark {} as finished".format(self)) 561 self.market.report.log_debug_action("Mark {} as finished".format(self))
564 return 562 return
565 if self.status == "closed": 563 if self.status == "closed":
566 if self.trade_type == "short" and self.action == "buy" and self.close_if_possible: 564 if self.trade_type == "short" and self.action == "buy" and self.close_if_possible:
567 self.market.close_margin_position(self.amount.currency, self.base_currency) 565 self.market.ccxt.close_margin_position(self.amount.currency, self.base_currency)
568 566
569 def fetch(self, force=False): 567 def fetch(self, force=False):
570 if TradeStore.debug: 568 if self.market.debug:
571 ReportStore.log_debug_action("Fetching {}".format(self)) 569 self.market.report.log_debug_action("Fetching {}".format(self))
572 return 570 return
573 if (not force and self.fetch_cache_timestamp is not None 571 if (not force and self.fetch_cache_timestamp is not None
574 and time.time() - self.fetch_cache_timestamp < 10): 572 and time.time() - self.fetch_cache_timestamp < 10):
575 return 573 return
576 self.fetch_cache_timestamp = time.time() 574 self.fetch_cache_timestamp = time.time()
577 575
578 result = self.market.fetch_order(self.id) 576 result = self.market.ccxt.fetch_order(self.id)
579 self.results.append(result) 577 self.results.append(result)
580 578
581 self.status = result["status"] 579 self.status = result["status"]
@@ -606,7 +604,7 @@ class Order:
606 604
607 def fetch_mouvements(self): 605 def fetch_mouvements(self):
608 try: 606 try:
609 mouvements = self.market.privatePostReturnOrderTrades({"orderNumber": self.id}) 607 mouvements = self.market.ccxt.privatePostReturnOrderTrades({"orderNumber": self.id})
610 except ExchangeError: 608 except ExchangeError:
611 mouvements = [] 609 mouvements = []
612 self.mouvements = [] 610 self.mouvements = []
@@ -616,11 +614,11 @@ class Order:
616 self.base_currency, mouvement_hash)) 614 self.base_currency, mouvement_hash))
617 615
618 def cancel(self): 616 def cancel(self):
619 if TradeStore.debug: 617 if self.market.debug:
620 ReportStore.log_debug_action("Mark {} as cancelled".format(self)) 618 self.market.report.log_debug_action("Mark {} as cancelled".format(self))
621 self.status = "canceled" 619 self.status = "canceled"
622 return 620 return
623 self.market.cancel_order(self.id) 621 self.market.ccxt.cancel_order(self.id)
624 self.fetch() 622 self.fetch()
625 623
626class Mouvement: 624class Mouvement:
@@ -663,6 +661,3 @@ class Mouvement:
663 date, self.action, self.total, self.total_in_base, 661 date, self.action, self.total, self.total_in_base,
664 fee_rate) 662 fee_rate)
665 663
666if __name__ == '__main__': # pragma: no cover
667 from market import market
668 h.print_orders(market)