tensortrade 1.0.0b0__py3-none-any.whl → 1.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tensortrade/__init__.py +23 -16
- tensortrade/agents/__init__.py +7 -7
- tensortrade/agents/a2c_agent.py +239 -237
- tensortrade/agents/agent.py +52 -49
- tensortrade/agents/dqn_agent.py +375 -202
- tensortrade/agents/parallel/__init__.py +5 -5
- tensortrade/agents/parallel/parallel_dqn_agent.py +172 -170
- tensortrade/agents/parallel/parallel_dqn_model.py +85 -83
- tensortrade/agents/parallel/parallel_dqn_optimizer.py +96 -90
- tensortrade/agents/parallel/parallel_dqn_trainer.py +97 -95
- tensortrade/agents/parallel/parallel_queue.py +95 -92
- tensortrade/agents/replay_memory.py +54 -52
- tensortrade/core/__init__.py +6 -6
- tensortrade/core/base.py +167 -173
- tensortrade/core/clock.py +48 -48
- tensortrade/core/component.py +129 -129
- tensortrade/core/context.py +182 -182
- tensortrade/core/exceptions.py +211 -211
- tensortrade/core/registry.py +45 -45
- tensortrade/data/__init__.py +1 -1
- tensortrade/data/cdd.py +152 -151
- tensortrade/env/__init__.py +2 -2
- tensortrade/env/default/__init__.py +96 -89
- tensortrade/env/default/actions.py +428 -399
- tensortrade/env/default/informers.py +14 -16
- tensortrade/env/default/observers.py +475 -284
- tensortrade/env/default/renderers.py +787 -586
- tensortrade/env/default/rewards.py +360 -240
- tensortrade/env/default/stoppers.py +33 -33
- tensortrade/env/generic/__init__.py +22 -22
- tensortrade/env/generic/components/__init__.py +13 -13
- tensortrade/env/generic/components/action_scheme.py +54 -54
- tensortrade/env/generic/components/informer.py +45 -45
- tensortrade/env/generic/components/observer.py +59 -59
- tensortrade/env/generic/components/renderer.py +86 -86
- tensortrade/env/generic/components/reward_scheme.py +44 -44
- tensortrade/env/generic/components/stopper.py +46 -46
- tensortrade/env/generic/environment.py +211 -163
- tensortrade/feed/__init__.py +5 -5
- tensortrade/feed/api/__init__.py +5 -5
- tensortrade/feed/api/boolean/__init__.py +44 -44
- tensortrade/feed/api/boolean/operations.py +20 -20
- tensortrade/feed/api/float/__init__.py +49 -48
- tensortrade/feed/api/float/accumulators.py +199 -199
- tensortrade/feed/api/float/imputation.py +40 -40
- tensortrade/feed/api/float/operations.py +233 -233
- tensortrade/feed/api/float/ordering.py +105 -105
- tensortrade/feed/api/float/utils.py +140 -140
- tensortrade/feed/api/float/window/__init__.py +3 -3
- tensortrade/feed/api/float/window/ewm.py +459 -452
- tensortrade/feed/api/float/window/expanding.py +189 -185
- tensortrade/feed/api/float/window/rolling.py +227 -217
- tensortrade/feed/api/generic/__init__.py +4 -4
- tensortrade/feed/api/generic/imputation.py +51 -51
- tensortrade/feed/api/generic/operators.py +118 -121
- tensortrade/feed/api/generic/reduce.py +119 -119
- tensortrade/feed/api/generic/warmup.py +54 -54
- tensortrade/feed/api/string/__init__.py +44 -43
- tensortrade/feed/api/string/operations.py +135 -131
- tensortrade/feed/core/__init__.py +3 -3
- tensortrade/feed/core/accessors.py +30 -30
- tensortrade/feed/core/base.py +634 -584
- tensortrade/feed/core/feed.py +120 -59
- tensortrade/feed/core/methods.py +37 -37
- tensortrade/feed/core/mixins.py +23 -23
- tensortrade/feed/core/operators.py +174 -174
- tensortrade/oms/__init__.py +2 -2
- tensortrade/oms/exchanges/__init__.py +1 -1
- tensortrade/oms/exchanges/exchange.py +176 -164
- tensortrade/oms/instruments/__init__.py +5 -5
- tensortrade/oms/instruments/exchange_pair.py +44 -44
- tensortrade/oms/instruments/instrument.py +161 -161
- tensortrade/oms/instruments/quantity.py +321 -318
- tensortrade/oms/instruments/trading_pair.py +58 -58
- tensortrade/oms/orders/__init__.py +13 -13
- tensortrade/oms/orders/broker.py +129 -125
- tensortrade/oms/orders/create.py +312 -312
- tensortrade/oms/orders/criteria.py +218 -218
- tensortrade/oms/orders/order.py +368 -368
- tensortrade/oms/orders/order_listener.py +62 -62
- tensortrade/oms/orders/order_spec.py +102 -102
- tensortrade/oms/orders/trade.py +159 -159
- tensortrade/oms/services/__init__.py +2 -2
- tensortrade/oms/services/execution/__init__.py +4 -4
- tensortrade/oms/services/execution/simulated.py +197 -183
- tensortrade/oms/services/slippage/__init__.py +21 -21
- tensortrade/oms/services/slippage/random_slippage_model.py +56 -56
- tensortrade/oms/services/slippage/slippage_model.py +46 -46
- tensortrade/oms/wallets/__init__.py +20 -20
- tensortrade/oms/wallets/ledger.py +92 -92
- tensortrade/oms/wallets/portfolio.py +330 -329
- tensortrade/oms/wallets/wallet.py +376 -365
- tensortrade/stochastic/__init__.py +12 -12
- tensortrade/stochastic/processes/brownian_motion.py +55 -55
- tensortrade/stochastic/processes/cox.py +103 -103
- tensortrade/stochastic/processes/fbm.py +88 -88
- tensortrade/stochastic/processes/gbm.py +129 -129
- tensortrade/stochastic/processes/heston.py +281 -281
- tensortrade/stochastic/processes/merton.py +91 -91
- tensortrade/stochastic/processes/ornstein_uhlenbeck.py +113 -113
- tensortrade/stochastic/utils/__init__.py +2 -2
- tensortrade/stochastic/utils/helpers.py +180 -179
- tensortrade/stochastic/utils/parameters.py +172 -172
- tensortrade/version.py +1 -1
- tensortrade-1.0.4.dist-info/METADATA +65 -0
- tensortrade-1.0.4.dist-info/RECORD +114 -0
- {tensortrade-1.0.0b0.dist-info → tensortrade-1.0.4.dist-info}/WHEEL +1 -1
- {tensortrade-1.0.0b0.dist-info → tensortrade-1.0.4.dist-info/licenses}/LICENSE +200 -200
- tensortrade-1.0.0b0.dist-info/METADATA +0 -74
- tensortrade-1.0.0b0.dist-info/RECORD +0 -114
- {tensortrade-1.0.0b0.dist-info → tensortrade-1.0.4.dist-info}/top_level.txt +0 -0
|
@@ -1,164 +1,176 @@
|
|
|
1
|
-
# Copyright 2019 The TensorTrade Authors.
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
from typing import Callable
|
|
17
|
-
from decimal import Decimal
|
|
18
|
-
|
|
19
|
-
from tensortrade.core import Component, TimedIdentifiable
|
|
20
|
-
from tensortrade.oms.instruments import TradingPair
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class ExchangeOptions:
|
|
24
|
-
"""An options class to specify the settings of an exchange.
|
|
25
|
-
|
|
26
|
-
Parameters
|
|
27
|
-
----------
|
|
28
|
-
commission : float, default 0.003
|
|
29
|
-
The percentage of the order size taken by the exchange.
|
|
30
|
-
min_trade_size : float, default 1e-6
|
|
31
|
-
The minimum trade size an order can have.
|
|
32
|
-
max_trade_size : float, default 1e6
|
|
33
|
-
The maximum trade size an order can have.
|
|
34
|
-
min_trade_price : float, default 1e-8
|
|
35
|
-
The minimum price an exchange can have.
|
|
36
|
-
max_trade_price : float, default 1e8
|
|
37
|
-
The maximum price an exchange can have.
|
|
38
|
-
is_live : bool, default False
|
|
39
|
-
Whether live orders should be submitted to the exchange.
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
def __init__(self,
|
|
43
|
-
commission: float = 0.003,
|
|
44
|
-
min_trade_size: float = 1e-6,
|
|
45
|
-
max_trade_size: float = 1e6,
|
|
46
|
-
min_trade_price: float = 1e-8,
|
|
47
|
-
max_trade_price: float = 1e8,
|
|
48
|
-
is_live: bool = False):
|
|
49
|
-
self.commission = commission
|
|
50
|
-
self.min_trade_size = min_trade_size
|
|
51
|
-
self.max_trade_size = max_trade_size
|
|
52
|
-
self.min_trade_price = min_trade_price
|
|
53
|
-
self.max_trade_price = max_trade_price
|
|
54
|
-
self.is_live = is_live
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
class Exchange(Component, TimedIdentifiable):
|
|
58
|
-
"""An abstract exchange for use within a trading environment.
|
|
59
|
-
|
|
60
|
-
Parameters
|
|
61
|
-
----------
|
|
62
|
-
name : str
|
|
63
|
-
The name of the exchange.
|
|
64
|
-
service : `Union[Callable, str]`
|
|
65
|
-
The service to be used for filling orders.
|
|
66
|
-
options : `ExchangeOptions`
|
|
67
|
-
The options used to specify the setting of the exchange.
|
|
68
|
-
"""
|
|
69
|
-
|
|
70
|
-
registered_name = "exchanges"
|
|
71
|
-
|
|
72
|
-
def __init__(self,
|
|
73
|
-
name: str,
|
|
74
|
-
service: Callable,
|
|
75
|
-
options: ExchangeOptions = None):
|
|
76
|
-
super().__init__()
|
|
77
|
-
self.name = name
|
|
78
|
-
self._service = service
|
|
79
|
-
self.options = options if options else ExchangeOptions()
|
|
80
|
-
self._price_streams = {}
|
|
81
|
-
|
|
82
|
-
def __call__(self, *streams) -> "Exchange":
|
|
83
|
-
"""Sets up the price streams used to generate the prices.
|
|
84
|
-
|
|
85
|
-
Parameters
|
|
86
|
-
----------
|
|
87
|
-
*streams
|
|
88
|
-
The positional arguments each being a price stream.
|
|
89
|
-
|
|
90
|
-
Returns
|
|
91
|
-
-------
|
|
92
|
-
`Exchange`
|
|
93
|
-
The exchange the price streams were passed in for.
|
|
94
|
-
"""
|
|
95
|
-
for s in streams:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
"""
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
1
|
+
# Copyright 2019 The TensorTrade Authors.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from typing import Callable
|
|
17
|
+
from decimal import Decimal
|
|
18
|
+
|
|
19
|
+
from tensortrade.core import Component, TimedIdentifiable
|
|
20
|
+
from tensortrade.oms.instruments import TradingPair
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ExchangeOptions:
|
|
24
|
+
"""An options class to specify the settings of an exchange.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
commission : float, default 0.003
|
|
29
|
+
The percentage of the order size taken by the exchange.
|
|
30
|
+
min_trade_size : float, default 1e-6
|
|
31
|
+
The minimum trade size an order can have.
|
|
32
|
+
max_trade_size : float, default 1e6
|
|
33
|
+
The maximum trade size an order can have.
|
|
34
|
+
min_trade_price : float, default 1e-8
|
|
35
|
+
The minimum price an exchange can have.
|
|
36
|
+
max_trade_price : float, default 1e8
|
|
37
|
+
The maximum price an exchange can have.
|
|
38
|
+
is_live : bool, default False
|
|
39
|
+
Whether live orders should be submitted to the exchange.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self,
|
|
43
|
+
commission: float = 0.003,
|
|
44
|
+
min_trade_size: float = 1e-6,
|
|
45
|
+
max_trade_size: float = 1e6,
|
|
46
|
+
min_trade_price: float = 1e-8,
|
|
47
|
+
max_trade_price: float = 1e8,
|
|
48
|
+
is_live: bool = False):
|
|
49
|
+
self.commission = commission
|
|
50
|
+
self.min_trade_size = min_trade_size
|
|
51
|
+
self.max_trade_size = max_trade_size
|
|
52
|
+
self.min_trade_price = min_trade_price
|
|
53
|
+
self.max_trade_price = max_trade_price
|
|
54
|
+
self.is_live = is_live
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Exchange(Component, TimedIdentifiable):
|
|
58
|
+
"""An abstract exchange for use within a trading environment.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
name : str
|
|
63
|
+
The name of the exchange.
|
|
64
|
+
service : `Union[Callable, str]`
|
|
65
|
+
The service to be used for filling orders.
|
|
66
|
+
options : `ExchangeOptions`
|
|
67
|
+
The options used to specify the setting of the exchange.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
registered_name = "exchanges"
|
|
71
|
+
|
|
72
|
+
def __init__(self,
|
|
73
|
+
name: str,
|
|
74
|
+
service: Callable,
|
|
75
|
+
options: ExchangeOptions = None):
|
|
76
|
+
super().__init__()
|
|
77
|
+
self.name = name
|
|
78
|
+
self._service = service
|
|
79
|
+
self.options = options if options else ExchangeOptions()
|
|
80
|
+
self._price_streams = {}
|
|
81
|
+
|
|
82
|
+
def __call__(self, *streams) -> "Exchange":
|
|
83
|
+
"""Sets up the price streams used to generate the prices.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
*streams
|
|
88
|
+
The positional arguments each being a price stream.
|
|
89
|
+
|
|
90
|
+
Returns
|
|
91
|
+
-------
|
|
92
|
+
`Exchange`
|
|
93
|
+
The exchange the price streams were passed in for.
|
|
94
|
+
"""
|
|
95
|
+
for s in streams:
|
|
96
|
+
# Standardize stream naming convention
|
|
97
|
+
pair = "".join([c if c.isalnum() else "/" for c in s.name])
|
|
98
|
+
# Use consistent naming: exchange_name:/base-quote
|
|
99
|
+
stream_name = f"{self.name}:/{s.name}"
|
|
100
|
+
self._price_streams[pair] = s.rename(stream_name)
|
|
101
|
+
return self
|
|
102
|
+
|
|
103
|
+
def streams(self) -> "List[Stream[float]]":
|
|
104
|
+
"""Gets the price streams for the exchange.
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
`List[Stream[float]]`
|
|
109
|
+
The price streams for the exchange.
|
|
110
|
+
"""
|
|
111
|
+
return list(self._price_streams.values())
|
|
112
|
+
|
|
113
|
+
def quote_price(self, trading_pair: "TradingPair") -> "Decimal":
|
|
114
|
+
"""The quote price of a trading pair on the exchange, denoted in the
|
|
115
|
+
core instrument.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
trading_pair : `TradingPair`
|
|
120
|
+
The trading pair to get the quote price for.
|
|
121
|
+
|
|
122
|
+
Returns
|
|
123
|
+
-------
|
|
124
|
+
`Decimal`
|
|
125
|
+
The quote price of the specified trading pair, denoted in the core instrument.
|
|
126
|
+
"""
|
|
127
|
+
price = Decimal(self._price_streams[str(trading_pair)].value)
|
|
128
|
+
if price == 0:
|
|
129
|
+
raise ValueError("Price of trading pair {} is 0. Please check your input data to make sure there always is "
|
|
130
|
+
"a valid (nonzero) price.".format(trading_pair))
|
|
131
|
+
|
|
132
|
+
price = price.quantize(Decimal(10) ** -trading_pair.base.precision)
|
|
133
|
+
if price == 0:
|
|
134
|
+
raise ValueError("Price quantized in base currency precision ({}) would amount to 0 {}. "
|
|
135
|
+
"Please consider defining a custom instrument with a higher precision."
|
|
136
|
+
.format(trading_pair.base.precision, trading_pair.base))
|
|
137
|
+
|
|
138
|
+
return price
|
|
139
|
+
|
|
140
|
+
def is_pair_tradable(self, trading_pair: 'TradingPair') -> bool:
|
|
141
|
+
"""Whether or not the specified trading pair is tradable on this
|
|
142
|
+
exchange.
|
|
143
|
+
|
|
144
|
+
Parameters
|
|
145
|
+
----------
|
|
146
|
+
trading_pair : `TradingPair`
|
|
147
|
+
The trading pair to test the tradability of.
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
bool
|
|
152
|
+
Whether or not the pair is tradable.
|
|
153
|
+
"""
|
|
154
|
+
return str(trading_pair) in self._price_streams.keys()
|
|
155
|
+
|
|
156
|
+
def execute_order(self, order: 'Order', portfolio: 'Portfolio') -> None:
|
|
157
|
+
"""Execute an order on the exchange.
|
|
158
|
+
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
order: `Order`
|
|
162
|
+
The order to execute.
|
|
163
|
+
portfolio : `Portfolio`
|
|
164
|
+
The portfolio to use.
|
|
165
|
+
"""
|
|
166
|
+
trade = self._service(
|
|
167
|
+
order=order,
|
|
168
|
+
base_wallet=portfolio.get_wallet(self.id, order.pair.base),
|
|
169
|
+
quote_wallet=portfolio.get_wallet(self.id, order.pair.quote),
|
|
170
|
+
current_price=self.quote_price(order.pair),
|
|
171
|
+
options=self.options,
|
|
172
|
+
clock=self.clock
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if trade:
|
|
176
|
+
order.fill(trade)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from .exchange_pair import ExchangePair
|
|
2
|
-
from .instrument import *
|
|
3
|
-
from .quantity import Quantity
|
|
4
|
-
from .trading_pair import TradingPair
|
|
5
|
-
|
|
1
|
+
from .exchange_pair import ExchangePair
|
|
2
|
+
from .instrument import *
|
|
3
|
+
from .quantity import Quantity
|
|
4
|
+
from .trading_pair import TradingPair
|
|
5
|
+
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
from decimal import Decimal
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class ExchangePair:
|
|
6
|
-
"""A pair of financial instruments to be traded on a specific exchange.
|
|
7
|
-
|
|
8
|
-
Parameters
|
|
9
|
-
----------
|
|
10
|
-
exchange : `Exchange`
|
|
11
|
-
An exchange that contains the `pair` for trading.
|
|
12
|
-
pair : `TradingPair`
|
|
13
|
-
A trading pair available on the `exchange`.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
def __init__(self, exchange: "Exchange", pair: "TradingPair"):
|
|
17
|
-
self.exchange = exchange
|
|
18
|
-
self.pair = pair
|
|
19
|
-
|
|
20
|
-
@property
|
|
21
|
-
def price(self) -> "Decimal":
|
|
22
|
-
"""The quoted price of the trading pair. (`Decimal`, read-only)"""
|
|
23
|
-
return self.exchange.quote_price(self.pair)
|
|
24
|
-
|
|
25
|
-
@property
|
|
26
|
-
def inverse_price(self) -> "Decimal":
|
|
27
|
-
"""The inverse price of the trading pair. (`Decimal, read-only)"""
|
|
28
|
-
quantization = Decimal(10) ** -self.pair.quote.precision
|
|
29
|
-
return Decimal(self.price ** Decimal(-1)).quantize(quantization)
|
|
30
|
-
|
|
31
|
-
def __hash__(self):
|
|
32
|
-
return hash(str(self))
|
|
33
|
-
|
|
34
|
-
def __eq__(self, other):
|
|
35
|
-
if isinstance(other, ExchangePair):
|
|
36
|
-
if str(self) == str(other):
|
|
37
|
-
return True
|
|
38
|
-
return False
|
|
39
|
-
|
|
40
|
-
def __str__(self):
|
|
41
|
-
return "{}:{}".format(self.exchange.name, self.pair)
|
|
42
|
-
|
|
43
|
-
def __repr__(self):
|
|
44
|
-
return str(self)
|
|
1
|
+
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ExchangePair:
|
|
6
|
+
"""A pair of financial instruments to be traded on a specific exchange.
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
exchange : `Exchange`
|
|
11
|
+
An exchange that contains the `pair` for trading.
|
|
12
|
+
pair : `TradingPair`
|
|
13
|
+
A trading pair available on the `exchange`.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, exchange: "Exchange", pair: "TradingPair"):
|
|
17
|
+
self.exchange = exchange
|
|
18
|
+
self.pair = pair
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def price(self) -> "Decimal":
|
|
22
|
+
"""The quoted price of the trading pair. (`Decimal`, read-only)"""
|
|
23
|
+
return self.exchange.quote_price(self.pair)
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def inverse_price(self) -> "Decimal":
|
|
27
|
+
"""The inverse price of the trading pair. (`Decimal, read-only)"""
|
|
28
|
+
quantization = Decimal(10) ** -self.pair.quote.precision
|
|
29
|
+
return Decimal(self.price ** Decimal(-1)).quantize(quantization)
|
|
30
|
+
|
|
31
|
+
def __hash__(self):
|
|
32
|
+
return hash(str(self))
|
|
33
|
+
|
|
34
|
+
def __eq__(self, other):
|
|
35
|
+
if isinstance(other, ExchangePair):
|
|
36
|
+
if str(self) == str(other):
|
|
37
|
+
return True
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
def __str__(self):
|
|
41
|
+
return "{}:{}".format(self.exchange.name, self.pair)
|
|
42
|
+
|
|
43
|
+
def __repr__(self):
|
|
44
|
+
return str(self)
|