Qubx 0.5.7__cp312-cp312-manylinux_2_39_x86_64.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.

Potentially problematic release.


This version of Qubx might be problematic. Click here for more details.

Files changed (100) hide show
  1. qubx/__init__.py +207 -0
  2. qubx/_nb_magic.py +100 -0
  3. qubx/backtester/__init__.py +5 -0
  4. qubx/backtester/account.py +145 -0
  5. qubx/backtester/broker.py +87 -0
  6. qubx/backtester/data.py +296 -0
  7. qubx/backtester/management.py +378 -0
  8. qubx/backtester/ome.py +296 -0
  9. qubx/backtester/optimization.py +201 -0
  10. qubx/backtester/simulated_data.py +558 -0
  11. qubx/backtester/simulator.py +362 -0
  12. qubx/backtester/utils.py +780 -0
  13. qubx/cli/__init__.py +0 -0
  14. qubx/cli/commands.py +67 -0
  15. qubx/connectors/ccxt/__init__.py +0 -0
  16. qubx/connectors/ccxt/account.py +495 -0
  17. qubx/connectors/ccxt/broker.py +132 -0
  18. qubx/connectors/ccxt/customizations.py +193 -0
  19. qubx/connectors/ccxt/data.py +612 -0
  20. qubx/connectors/ccxt/exceptions.py +17 -0
  21. qubx/connectors/ccxt/factory.py +93 -0
  22. qubx/connectors/ccxt/utils.py +307 -0
  23. qubx/core/__init__.py +0 -0
  24. qubx/core/account.py +251 -0
  25. qubx/core/basics.py +850 -0
  26. qubx/core/context.py +420 -0
  27. qubx/core/exceptions.py +38 -0
  28. qubx/core/helpers.py +480 -0
  29. qubx/core/interfaces.py +1150 -0
  30. qubx/core/loggers.py +514 -0
  31. qubx/core/lookups.py +475 -0
  32. qubx/core/metrics.py +1512 -0
  33. qubx/core/mixins/__init__.py +13 -0
  34. qubx/core/mixins/market.py +94 -0
  35. qubx/core/mixins/processing.py +428 -0
  36. qubx/core/mixins/subscription.py +203 -0
  37. qubx/core/mixins/trading.py +88 -0
  38. qubx/core/mixins/universe.py +270 -0
  39. qubx/core/series.cpython-312-x86_64-linux-gnu.so +0 -0
  40. qubx/core/series.pxd +125 -0
  41. qubx/core/series.pyi +118 -0
  42. qubx/core/series.pyx +988 -0
  43. qubx/core/utils.cpython-312-x86_64-linux-gnu.so +0 -0
  44. qubx/core/utils.pyi +6 -0
  45. qubx/core/utils.pyx +62 -0
  46. qubx/data/__init__.py +25 -0
  47. qubx/data/helpers.py +416 -0
  48. qubx/data/readers.py +1562 -0
  49. qubx/data/tardis.py +100 -0
  50. qubx/gathering/simplest.py +88 -0
  51. qubx/math/__init__.py +3 -0
  52. qubx/math/stats.py +129 -0
  53. qubx/pandaz/__init__.py +23 -0
  54. qubx/pandaz/ta.py +2757 -0
  55. qubx/pandaz/utils.py +638 -0
  56. qubx/resources/instruments/symbols-binance.cm.json +1 -0
  57. qubx/resources/instruments/symbols-binance.json +1 -0
  58. qubx/resources/instruments/symbols-binance.um.json +1 -0
  59. qubx/resources/instruments/symbols-bitfinex.f.json +1 -0
  60. qubx/resources/instruments/symbols-bitfinex.json +1 -0
  61. qubx/resources/instruments/symbols-kraken.f.json +1 -0
  62. qubx/resources/instruments/symbols-kraken.json +1 -0
  63. qubx/ta/__init__.py +0 -0
  64. qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so +0 -0
  65. qubx/ta/indicators.pxd +149 -0
  66. qubx/ta/indicators.pyi +41 -0
  67. qubx/ta/indicators.pyx +787 -0
  68. qubx/trackers/__init__.py +3 -0
  69. qubx/trackers/abvanced.py +236 -0
  70. qubx/trackers/composite.py +146 -0
  71. qubx/trackers/rebalancers.py +129 -0
  72. qubx/trackers/riskctrl.py +641 -0
  73. qubx/trackers/sizers.py +235 -0
  74. qubx/utils/__init__.py +5 -0
  75. qubx/utils/_pyxreloader.py +281 -0
  76. qubx/utils/charting/lookinglass.py +1057 -0
  77. qubx/utils/charting/mpl_helpers.py +1183 -0
  78. qubx/utils/marketdata/binance.py +284 -0
  79. qubx/utils/marketdata/ccxt.py +90 -0
  80. qubx/utils/marketdata/dukas.py +130 -0
  81. qubx/utils/misc.py +541 -0
  82. qubx/utils/ntp.py +63 -0
  83. qubx/utils/numbers_utils.py +7 -0
  84. qubx/utils/orderbook.py +491 -0
  85. qubx/utils/plotting/__init__.py +0 -0
  86. qubx/utils/plotting/dashboard.py +150 -0
  87. qubx/utils/plotting/data.py +137 -0
  88. qubx/utils/plotting/interfaces.py +25 -0
  89. qubx/utils/plotting/renderers/__init__.py +0 -0
  90. qubx/utils/plotting/renderers/plotly.py +0 -0
  91. qubx/utils/runner/__init__.py +1 -0
  92. qubx/utils/runner/_jupyter_runner.pyt +60 -0
  93. qubx/utils/runner/accounts.py +88 -0
  94. qubx/utils/runner/configs.py +65 -0
  95. qubx/utils/runner/runner.py +470 -0
  96. qubx/utils/time.py +312 -0
  97. qubx-0.5.7.dist-info/METADATA +105 -0
  98. qubx-0.5.7.dist-info/RECORD +100 -0
  99. qubx-0.5.7.dist-info/WHEEL +4 -0
  100. qubx-0.5.7.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,284 @@
1
+ import os
2
+ from typing import Any, Callable, Dict, List, Tuple
3
+ from os import unlink
4
+ import numpy as np
5
+ import pandas as pd
6
+ from os.path import exists, join, split, basename
7
+ from tqdm.notebook import tqdm
8
+ import requests
9
+ from collections import defaultdict
10
+
11
+ from qubx import logger
12
+ from qubx.utils.misc import makedirs, get_local_qubx_folder
13
+ from qubx.pandaz import generate_equal_date_ranges, srows
14
+
15
+
16
+ DEFALT_LOCAL_FILE_STORAGE = makedirs(get_local_qubx_folder(), 'data/import/binance_history/')
17
+ DEFALT_LOCAL_CSV_STORAGE = makedirs(get_local_qubx_folder(), 'data/binance/')
18
+
19
+ # _DEFAULT_MARKET_DATA_DB = 'md'
20
+ BINANCE_DATA_STORAGE = "https://s3-ap-northeast-1.amazonaws.com"
21
+ BINANCE_DATA_URL = "https://data.binance.vision/"
22
+
23
+
24
+ def get_binance_symbol_info_for_type(market_types: List[str]) -> Dict[str, Dict[str, Any]]:
25
+ """
26
+ Get list of all symbols from binance for given list of market types:
27
+ possible types are: SPOT, FUTURES, COINSFUTURES
28
+
29
+ >>> get_binance_symbol_info_for_type('FUTURES')
30
+
31
+ :param market_type: SPOT, FUTURES (UM) or COINSFUTURES (CM)
32
+ """
33
+ from binance.client import Client
34
+
35
+ client = Client()
36
+ infos = {}
37
+ for market_type in (market_types if not isinstance(market_types, str) else [market_types]):
38
+ if market_type in ['FUTURES', 'UM']:
39
+ infos['binance.um'] = client.futures_exchange_info()
40
+
41
+ elif market_type in ['COINSFUTURES', 'CM']:
42
+ infos['binance.cm'] = client.futures_coin_exchange_info()
43
+
44
+ elif market_type == 'SPOT':
45
+ infos['binance'] = client.get_exchange_info()
46
+ else:
47
+ raise ValueError("Only 'FUTURES | UM', 'COINSFUTURES | CM' or 'SPOT' are supported for market_type")
48
+
49
+ return infos
50
+
51
+
52
+ def fetch_file(url, local_file_storage, chunk_size=1024*1024, progress_bar=True):
53
+ """
54
+ Load file from url and store it to specified storage
55
+ """
56
+ file = split(url)[-1]
57
+
58
+ # if dest location not exists create it
59
+ if not exists(local_file_storage):
60
+ makedirs(local_file_storage)
61
+
62
+ response = requests.get(url, stream=True)
63
+ if response.status_code != 200:
64
+ logger.warning(f"Error while fetching {url}: {response.status_code}")
65
+ return None
66
+ fpath = join(local_file_storage, file)
67
+ fpath_temp = join(local_file_storage, f"{file}_tmp")
68
+ with open(fpath_temp, "wb") as handle:
69
+ iters = response.iter_content(chunk_size=chunk_size)
70
+ for data in tqdm(iters) if progress_bar else iters:
71
+ handle.write(data)
72
+ os.rename(fpath_temp, fpath)
73
+ return fpath
74
+
75
+
76
+ def get_trades_files(symbol: str, instr_type: str, instr_subtype: str):
77
+ """
78
+ Get list of trades files for specified instrument from Binance datastorage
79
+ """
80
+ if instr_type.lower() == 'spot':
81
+ instr_subtype = ''
82
+ filter_str = join("data", instr_type.lower(), instr_subtype.lower(), "monthly", "trades", symbol.upper())
83
+ pg = requests.get(f"{BINANCE_DATA_STORAGE}/data.binance.vision?prefix={filter_str}/")
84
+ info = pd.read_xml(pg.text)
85
+ return [k for k in info.Key.dropna() if k.endswith('.zip')]
86
+
87
+
88
+ def load_trades_for(symbol, instr_type='futures', instr_subtype='um', local_file_storage=DEFALT_LOCAL_FILE_STORAGE):
89
+ """
90
+ Load trades from Binance data storage
91
+ >>> load_trades_for('ETHUSDT', 'futures', 'um')
92
+ """
93
+ local_file_storage = makedirs(local_file_storage)
94
+
95
+ f_list = get_trades_files(symbol, instr_type, instr_subtype)
96
+ for r_file in tqdm(f_list):
97
+ dest_dir = join(local_file_storage, symbol.upper())
98
+ dest_file = join(dest_dir, basename(r_file))
99
+ if not exists(dest_file):
100
+ fetch_file(join(BINANCE_DATA_URL, r_file), dest_dir)
101
+ else:
102
+ logger.info(f"{dest_file} already loaded, skipping ...")
103
+
104
+
105
+ def parse_kl_file(fpath: str) -> pd.DataFrame:
106
+ _reader = lambda fp, hdr: pd.read_csv(fp, names = [
107
+ 'open_time', 'open', 'high', 'low', 'close', 'volume', 'close_time',
108
+ 'quote_volume', 'count', 'taker_buy_volume', 'taker_buy_quote_volume', 'ignore'
109
+ ],
110
+ usecols=[
111
+ 'open_time', 'open', 'high', 'low', 'close', 'volume', 'quote_volume',
112
+ 'count', 'taker_buy_volume', 'taker_buy_quote_volume',
113
+ ],
114
+ index_col='open_time', header=hdr
115
+ )
116
+ d = _reader(fpath, None)
117
+
118
+ # - if there is header
119
+ if isinstance(d.index[0], str):
120
+ d = d.iloc[1:, :].astype(float)
121
+
122
+ d.index = pd.to_datetime(d.index, unit='ms').rename('timestamp')
123
+ return d
124
+
125
+
126
+ def load_binance_kl_history(symbol: str, start: str, stop: str = 'now',
127
+ instr_type='futures', instr_subtype='um',
128
+ timeframe='1m', temp_storage=DEFALT_LOCAL_FILE_STORAGE) -> pd.DataFrame:
129
+ """
130
+ Loads binance 1m KLine history from AWS storage
131
+
132
+ Parameters
133
+ ----------
134
+ symbol : str
135
+ The symbol to load data for
136
+ start : str
137
+ The start date in format %Y-%m-%d
138
+ stop : str, optional
139
+ The end date in format %Y-%m-%d, by default 'now'
140
+ instr_type : str, optional
141
+ The instrument type, one of 'futures', 'spot', by default 'futures'
142
+ instr_subtype : str, optional
143
+ The instrument subtype, one of 'um', 'cm', by default 'um'
144
+ timeframe : str, optional
145
+ The timeframe, one of '1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w', '1M', by default '1m'
146
+ temp_storage : str, optional
147
+ The temporary storage location, by default 'DEFALT_LOCAL_FILE_STORAGE'
148
+
149
+ Returns
150
+ -------
151
+ pd.DataFrame
152
+ The loaded data
153
+ """
154
+ start = pd.Timestamp(start)
155
+ stop = pd.Timestamp(stop)
156
+ if instr_type.lower() == 'futures':
157
+ subt = f"{instr_type}/{instr_subtype}"
158
+ else:
159
+ subt = 'spot'
160
+ temp_storage = join(temp_storage, subt)
161
+
162
+ def _loader(start: pd.Timestamp, stop: pd.Timestamp, f_units: str):
163
+ curr_date = pd.Timestamp('now').ceil('1d')
164
+ data = pd.DataFrame()
165
+ continue_from = None
166
+ for t, _ in generate_equal_date_ranges(start, stop, 1, f_units[0].upper()):
167
+ if f_units =='monthly':
168
+ dt = pd.Timestamp(t)
169
+ # stop when we got into current month
170
+ if dt.year == curr_date.year and dt.month == curr_date.month:
171
+ continue_from = t
172
+ break
173
+ dt = dt.strftime("%Y-%m")
174
+ else:
175
+ dt = pd.Timestamp(t).strftime("%Y-%m-%d")
176
+
177
+ fname = f"{symbol.upper()}-{timeframe}-{dt}.zip"
178
+ zf = join(temp_storage, fname)
179
+
180
+ if not exists(zf):
181
+ logger.info(f'[green]{(symbol)}[/green] {instr_subtype} {instr_type} loading data for [yellow]{dt}[/yellow] -> [red]{fname}[/red]')
182
+ u = f'{BINANCE_DATA_URL}data/{subt}/{f_units}/klines/{symbol.upper()}/{timeframe}/{fname}'
183
+ zf = fetch_file(u, temp_storage, progress_bar=False)
184
+ else:
185
+ logger.info(f'[green]{symbol}[/green] {instr_subtype} {instr_type} parsing data from [red]{fname}[/red]')
186
+ if zf and exists(zf):
187
+ try:
188
+ data = srows(data, parse_kl_file(zf), keep='last')
189
+ except Exception as err:
190
+ logger.warning(err)
191
+ unlink(zf)
192
+ return data, continue_from
193
+
194
+ # - load by months
195
+ data, cont_time = _loader(start, stop, 'monthly')
196
+
197
+ # - rest data load by days
198
+ if cont_time is not None:
199
+ data_cont, cont_time = _loader(cont_time, stop, 'daily')
200
+ data = srows(data, data_cont, keep='last')
201
+
202
+ return data
203
+
204
+
205
+ def update_binance_data_storage(coins=[], quoted_in=['USDT'], market='futures', data_storage=DEFALT_LOCAL_CSV_STORAGE):
206
+ """
207
+ Fetch data from the Binance data storage and save it as local csv files
208
+ TODO: csv is just temporary solution and we need to keep data in DB
209
+ """
210
+ from binance.client import Client, HistoricalKlinesType
211
+ info = get_binance_symbol_info_for_type(['UM', 'CM', 'SPOT'])
212
+
213
+ if market.lower() == 'futures':
214
+ for sy in info['binance.um']['symbols']:
215
+ if sy['quoteAsset'] in quoted_in:
216
+ if coins and sy['baseAsset'] not in coins:
217
+ continue
218
+ symbol = sy['symbol']
219
+ start = pd.Timestamp(sy['onboardDate'], unit='ms')
220
+ data = load_binance_kl_history(symbol, start.strftime('%Y-%m-%d'), instr_type='futures', instr_subtype='um')
221
+ # - update in mongo db
222
+ if data is not None and not data.empty:
223
+ data.to_csv(join(data_storage, 'BINANCE.UM'))
224
+ # path = f'm1/BINANCEF:{symbol}'
225
+ # z_del(path, dbname=_DEFAULT_MARKET_DATA_DB)
226
+ # z_save(path, data, dbname=_DEFAULT_MARKET_DATA_DB)
227
+ del data
228
+
229
+ if market.lower() == 'spot':
230
+ client = Client()
231
+ for sy in info['binance']['symbols']:
232
+ if sy['quoteAsset'] in quoted_in:
233
+ if coins and sy['baseAsset'] not in coins:
234
+ continue
235
+ symbol = sy['symbol']
236
+ # - some dirty way to get historical data start for spot
237
+ d = client.get_historical_klines(
238
+ symbol, '1M', '2017-01-01', '2100-01-01', klines_type=HistoricalKlinesType.SPOT, limit=1000
239
+ )
240
+ start = pd.Timestamp(d[0][0], unit='ms')
241
+ data = load_binance_kl_history(symbol, start.strftime('%Y-%m-%d'), instr_type='spot', instr_subtype='-')
242
+ # - update in mongo db
243
+ if data is not None and not data.empty:
244
+ data.to_csv(join(data_storage, 'BINANCE'))
245
+ # path = f'm1/BINANCE:{symbol}'
246
+ # z_del(path, dbname=_DEFAULT_MARKET_DATA_DB)
247
+ # z_save(path, data, dbname=_DEFAULT_MARKET_DATA_DB)
248
+ del data
249
+
250
+
251
+ def load_binance_markets_info() -> Tuple[pd.DataFrame, Dict[str, pd.DataFrame]]:
252
+ """
253
+ Load binance market info using SPA (non-documented)
254
+ """
255
+ resp = requests.get('https://www.binance.com/bapi/asset/v2/public/asset-service/product/get-products?includeEtf=false')
256
+ data = resp.json()
257
+
258
+ market_caps = {}
259
+ m_tags = defaultdict(list)
260
+ for r in data['data']:
261
+ symb = r['s']
262
+ market_caps[symb] = {
263
+ 'Symbol': symb,
264
+ 'MarketCap': float(r['cs']) * float(r['c']) / 1_000_000,
265
+ 'Coin': r['b'],
266
+ 'Quoted': r['q'],
267
+ 'Name': r['an'],
268
+ 'Tags': r['tags'],
269
+ }
270
+ for t in r['tags']:
271
+ m_tags[t].append({
272
+ 'Symbol': symb,
273
+ 'MarketCap': float(r['cs']) * float(r['c']) / 1_000_000,
274
+ 'Coin': r['b'],
275
+ 'Quoted': r['q'],
276
+ 'Name': r['an']
277
+ })
278
+ mktcap = pd.DataFrame.from_dict(market_caps, orient='index').sort_values('MarketCap', ascending=False)
279
+
280
+ markets_tags = {}
281
+ for t, m in m_tags.items():
282
+ markets_tags[t.lower() if t else 'none'] = pd.DataFrame.from_records(m).sort_values('MarketCap', ascending=False)
283
+
284
+ return mktcap, markets_tags
@@ -0,0 +1,90 @@
1
+ from typing import Any
2
+
3
+ import pandas as pd
4
+
5
+ from qubx.core.basics import AssetType, Instrument, MarketType
6
+
7
+
8
+ def ccxt_build_qubx_exchange_name(ccxt_exchange: str, market_type: str | None = None, is_linear: bool = True) -> str:
9
+ """
10
+ Build a Qubx exchange name from a ccxt exchange name and a market dictionary.
11
+ Parameters:
12
+ ccxt_exchange (str): The ccxt exchange name.
13
+ market (dict): The market dictionary.
14
+ Returns:
15
+ str: The Qubx exchange name.
16
+ """
17
+ if ccxt_exchange == "BINANCE.PM":
18
+ if market_type in ["spot", "margin"]:
19
+ return "BINANCE"
20
+ elif market_type == "swap" and is_linear:
21
+ return "BINANCE.UM"
22
+ elif market_type == "swap" and not is_linear:
23
+ return "BINANCE.CM"
24
+ else:
25
+ return "BINANCE.UM"
26
+ else:
27
+ # for not just return the input exchange and extend later if needed
28
+ return ccxt_exchange
29
+
30
+
31
+ def ccxt_symbol_to_instrument(ccxt_exchange_name: str, market: dict[str, Any]) -> Instrument:
32
+ exchange = ccxt_build_qubx_exchange_name(ccxt_exchange_name, market["type"], market.get("linear", True))
33
+ inner_info = market["info"]
34
+ maint_margin = 0.0
35
+ required_margin = 0.0
36
+ liquidation_fee = 0.0
37
+ if "marginLevels" in inner_info:
38
+ margins = inner_info["marginLevels"][0]
39
+ maint_margin = float(margins["maintenanceMargin"])
40
+ required_margin = float(margins["initialMargin"])
41
+ if "maintMarginPercent" in inner_info:
42
+ maint_margin = float(inner_info["maintMarginPercent"]) / 100
43
+ if "requiredMarginPercent" in inner_info:
44
+ required_margin = float(inner_info["requiredMarginPercent"]) / 100
45
+ if "liquidationFee" in inner_info:
46
+ liquidation_fee = float(inner_info["liquidationFee"])
47
+
48
+ # symbol = market["id"]
49
+ # let's use unified symbol format across all exchanges / types: BASEQUOTE
50
+ symbol = market["base"] + market["quote"]
51
+
52
+ # add some exchange specific formatting of symbol name
53
+ tick_size = float(market["precision"]["price"] or 0.0)
54
+ lot_size = float(market["precision"]["amount"] or 0.0)
55
+ min_size = float(market["limits"]["amount"]["min"] or 0.0)
56
+ min_notional = float(market["limits"]["cost"]["min"] or 0.0)
57
+
58
+ if exchange in ["BITFINEX", "BITFINEX.F"]:
59
+ if symbol.startswith("t"):
60
+ symbol = symbol[1:]
61
+ symbol = symbol.replace(":", "-")
62
+ tick_size = 10**-tick_size
63
+ lot_size = 10**-lot_size
64
+ min_size = 10**-min_size
65
+ min_notional = 10**-min_notional
66
+
67
+ return Instrument(
68
+ symbol=symbol,
69
+ asset_type=AssetType.CRYPTO,
70
+ market_type=MarketType[market["type"].upper()],
71
+ exchange=exchange,
72
+ base=market["base"],
73
+ quote=market["quote"],
74
+ settle=market["settle"],
75
+ exchange_symbol=market["id"],
76
+ tick_size=tick_size,
77
+ lot_size=lot_size,
78
+ min_size=min_size,
79
+ min_notional=min_notional,
80
+ initial_margin=required_margin,
81
+ maint_margin=maint_margin,
82
+ liquidation_fee=liquidation_fee,
83
+ contract_size=float(market.get("contractSize", 1.0) or 1.0),
84
+ onboard_date=pd.Timestamp(int(inner_info["onboardDate"]), unit="ms") if "onboardDate" in inner_info else None,
85
+ delivery_date=(
86
+ pd.Timestamp(int(market["expiryDatetime"]), unit="ms")
87
+ if "expiryDatetime" in inner_info
88
+ else pd.Timestamp("2100-01-01T00:00:00")
89
+ ),
90
+ )
@@ -0,0 +1,130 @@
1
+ from qubx.core.basics import Instrument, AssetType, MarketType
2
+
3
+ SAMPLE_INSTRUMENTS = [
4
+ Instrument(
5
+ symbol="EURUSD",
6
+ asset_type=AssetType.FX,
7
+ market_type=MarketType.SPOT,
8
+ exchange="DUKAS",
9
+ base="EUR",
10
+ quote="USD",
11
+ settle="USD",
12
+ exchange_symbol="EURUSD",
13
+ tick_size=0.00001,
14
+ lot_size=1000,
15
+ min_size=1000,
16
+ min_notional=0.0,
17
+ initial_margin=0.0,
18
+ maint_margin=0.0,
19
+ liquidation_fee=0.0,
20
+ contract_size=1.0,
21
+ ),
22
+ Instrument(
23
+ symbol="GBPUSD",
24
+ asset_type=AssetType.FX,
25
+ market_type=MarketType.SPOT,
26
+ exchange="DUKAS",
27
+ base="GBP",
28
+ quote="USD",
29
+ settle="USD",
30
+ exchange_symbol="GBPUSD",
31
+ tick_size=0.00001,
32
+ lot_size=1000,
33
+ min_size=1000,
34
+ min_notional=0.0,
35
+ initial_margin=0.0,
36
+ maint_margin=0.0,
37
+ liquidation_fee=0.0,
38
+ contract_size=1.0,
39
+ ),
40
+ Instrument(
41
+ symbol="USDJPY",
42
+ asset_type=AssetType.FX,
43
+ market_type=MarketType.SPOT,
44
+ exchange="DUKAS",
45
+ base="USD",
46
+ quote="JPY",
47
+ settle="USD",
48
+ exchange_symbol="USDJPY",
49
+ tick_size=0.001,
50
+ lot_size=1000,
51
+ min_size=1000,
52
+ min_notional=0.0,
53
+ initial_margin=0.0,
54
+ maint_margin=0.0,
55
+ liquidation_fee=0.0,
56
+ contract_size=1.0,
57
+ ),
58
+ Instrument(
59
+ symbol="USDCAD",
60
+ asset_type=AssetType.FX,
61
+ market_type=MarketType.SPOT,
62
+ exchange="DUKAS",
63
+ base="USD",
64
+ quote="CAD",
65
+ settle="USD",
66
+ exchange_symbol="USDCAD",
67
+ tick_size=0.00001,
68
+ lot_size=1000,
69
+ min_size=1000,
70
+ min_notional=0.0,
71
+ initial_margin=0.0,
72
+ maint_margin=0.0,
73
+ liquidation_fee=0.0,
74
+ contract_size=1.0,
75
+ ),
76
+ Instrument(
77
+ symbol="AUDUSD",
78
+ asset_type=AssetType.FX,
79
+ market_type=MarketType.SPOT,
80
+ exchange="DUKAS",
81
+ base="AUD",
82
+ quote="USD",
83
+ settle="USD",
84
+ exchange_symbol="AUDUSD",
85
+ tick_size=0.00001,
86
+ lot_size=1000,
87
+ min_size=1000,
88
+ min_notional=0.0,
89
+ initial_margin=0.0,
90
+ maint_margin=0.0,
91
+ liquidation_fee=0.0,
92
+ contract_size=1.0,
93
+ ),
94
+ Instrument(
95
+ symbol="USDPLN",
96
+ asset_type=AssetType.FX,
97
+ market_type=MarketType.SPOT,
98
+ exchange="DUKAS",
99
+ base="USD",
100
+ quote="PLN",
101
+ settle="USD",
102
+ exchange_symbol="USDPLN",
103
+ tick_size=0.00001,
104
+ lot_size=1000,
105
+ min_size=1000,
106
+ min_notional=0.0,
107
+ initial_margin=0.0,
108
+ maint_margin=0.0,
109
+ liquidation_fee=0.0,
110
+ contract_size=1.0,
111
+ ),
112
+ Instrument(
113
+ symbol="EURGBP",
114
+ asset_type=AssetType.FX,
115
+ market_type=MarketType.SPOT,
116
+ exchange="DUKAS",
117
+ base="EUR",
118
+ quote="GBP",
119
+ settle="USD",
120
+ exchange_symbol="EURGBP",
121
+ tick_size=0.00001,
122
+ lot_size=1000,
123
+ min_size=1000,
124
+ min_notional=0.0,
125
+ initial_margin=0.0,
126
+ maint_margin=0.0,
127
+ liquidation_fee=0.0,
128
+ contract_size=1.0,
129
+ ),
130
+ ]