1 from ccxt
import ExchangeError
2 import ccxt_wrapper
as ccxt
13 def __init__(self
, ccxt_instance
, debug
=False):
15 self
.ccxt
= ccxt_instance
16 self
.ccxt
._market
= self
17 self
.report
= ReportStore(self
)
18 self
.trades
= TradeStore(self
)
19 self
.balances
= BalanceStore(self
)
22 def from_config(cls
, config
, debug
=False):
23 config
["apiKey"] = config
.pop("key")
25 ccxt_instance
= ccxt
.poloniexE(config
)
27 # For requests logging
28 ccxt_instance
.session
.origin_request
= ccxt_instance
.session
.request
29 ccxt_instance
.session
._parent
= ccxt_instance
31 def request_wrap(self
, *args
, **kwargs
):
32 r
= self
.origin_request(*args
, **kwargs
)
33 self
._parent
._market
.report
.log_http_request(args
[0],
34 args
[1], kwargs
["data"], kwargs
["headers"], r
)
36 ccxt_instance
.session
.request
= request_wrap
.__get
__(ccxt_instance
.session
,
37 ccxt_instance
.session
.__class
__)
39 return cls(ccxt_instance
, debug
=debug
)
41 def move_balances(self
):
45 for currency
in self
.balances
.all
:
46 if self
.balances
.all
[currency
].margin_free
!= 0:
47 needed_in_margin
[currency
] = 0
48 for trade
in self
.trades
.all
:
49 if trade
.value_to
.currency
not in needed_in_margin
:
50 needed_in_margin
[trade
.value_to
.currency
] = 0
51 if trade
.trade_type
== "short":
52 needed_in_margin
[trade
.value_to
.currency
] += abs(trade
.value_to
)
53 for currency
, needed
in needed_in_margin
.items():
54 current_balance
= self
.balances
.all
[currency
].margin_free
55 moving_to_margin
[currency
] = (needed
- current_balance
)
56 delta
= moving_to_margin
[currency
].value
58 self
.report
.log_debug_action("Moving {} from exchange to margin".format(moving_to_margin
[currency
]))
61 self
.ccxt
.transfer_balance(currency
, delta
, "exchange", "margin")
63 self
.ccxt
.transfer_balance(currency
, -delta
, "margin", "exchange")
64 self
.report
.log_move_balances(needed_in_margin
, moving_to_margin
)
66 self
.balances
.fetch_balances()
70 if self
.ccxt
.__class
__ not in self
.fees_cache
:
71 self
.fees_cache
[self
.ccxt
.__class
__] = self
.ccxt
.fetch_fees()
72 return self
.fees_cache
[self
.ccxt
.__class
__]
75 ticker_cache_timestamp
= time
.time()
76 def get_ticker(self
, c1
, c2
, refresh
=False):
80 "average": (1/ticker
["bid"] + 1/ticker
["ask"]) / 2,
83 def augment_ticker(ticker
):
86 "average": (ticker
["bid"] + ticker
["ask"] ) / 2,
89 if time
.time() - self
.ticker_cache_timestamp
> 5:
90 self
.ticker_cache
= {}
91 self
.ticker_cache_timestamp
= time
.time()
93 if (c1
, c2
, self
.ccxt
.__class
__) in self
.ticker_cache
:
94 return self
.ticker_cache
[(c1
, c2
, self
.ccxt
.__class
__)]
95 if (c2
, c1
, self
.ccxt
.__class
__) in self
.ticker_cache
:
96 return invert(self
.ticker_cache
[(c2
, c1
, self
.ccxt
.__class
__)])
99 self
.ticker_cache
[(c1
, c2
, self
.ccxt
.__class
__)] = self
.ccxt
.fetch_ticker("{}/{}".format(c1
, c2
))
100 augment_ticker(self
.ticker_cache
[(c1
, c2
, self
.ccxt
.__class
__)])
101 except ExchangeError
:
103 self
.ticker_cache
[(c2
, c1
, self
.ccxt
.__class
__)] = self
.ccxt
.fetch_ticker("{}/{}".format(c2
, c1
))
104 augment_ticker(self
.ticker_cache
[(c2
, c1
, self
.ccxt
.__class
__)])
105 except ExchangeError
:
106 self
.ticker_cache
[(c1
, c2
, self
.ccxt
.__class
__)] = None
107 return self
.get_ticker(c1
, c2
)
109 def follow_orders(self
, sleep
=None):
111 sleep
= 7 if self
.debug
else 30
113 self
.report
.log_debug_action("Set follow_orders tick to {}s".format(sleep
))
115 self
.report
.log_stage("follow_orders_begin")
116 while len(self
.trades
.all_orders(state
="open")) > 0:
119 open_orders
= self
.trades
.all_orders(state
="open")
120 self
.report
.log_stage("follow_orders_tick_{}".format(tick
))
121 self
.report
.log_orders(open_orders
, tick
=tick
)
122 for order
in open_orders
:
123 if order
.get_status() != "open":
124 self
.report
.log_order(order
, tick
, finished
=True)
126 order
.trade
.update_order(order
, tick
)
127 self
.report
.log_stage("follow_orders_end")
129 def prepare_trades(self
, base_currency
="BTC", liquidity
="medium", compute_value
="average"):
130 self
.report
.log_stage("prepare_trades")
131 values_in_base
= self
.balances
.in_currency(base_currency
, compute_value
=compute_value
)
132 total_base_value
= sum(values_in_base
.values())
133 new_repartition
= self
.balances
.dispatch_assets(total_base_value
, liquidity
=liquidity
)
134 # Recompute it in case we have new currencies
135 values_in_base
= self
.balances
.in_currency(base_currency
, compute_value
=compute_value
)
136 self
.trades
.compute_trades(values_in_base
, new_repartition
)
138 def update_trades(self
, base_currency
="BTC", liquidity
="medium", compute_value
="average", only
=None):
139 self
.report
.log_stage("update_trades")
140 values_in_base
= self
.balances
.in_currency(base_currency
, compute_value
=compute_value
)
141 total_base_value
= sum(values_in_base
.values())
142 new_repartition
= self
.balances
.dispatch_assets(total_base_value
, liquidity
=liquidity
)
143 self
.trades
.compute_trades(values_in_base
, new_repartition
, only
=only
)
145 def prepare_trades_to_sell_all(self
, base_currency
="BTC", compute_value
="average"):
146 self
.report
.log_stage("prepare_trades_to_sell_all")
147 values_in_base
= self
.balances
.in_currency(base_currency
, compute_value
=compute_value
)
148 total_base_value
= sum(values_in_base
.values())
149 new_repartition
= self
.balances
.dispatch_assets(total_base_value
, repartition
={ base_currency: (1, "long") }
)
150 self
.trades
.compute_trades(values_in_base
, new_repartition
)