Qubx 0.6.10__tar.gz → 0.6.12__tar.gz

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 (141) hide show
  1. {qubx-0.6.10 → qubx-0.6.12}/PKG-INFO +1 -1
  2. {qubx-0.6.10 → qubx-0.6.12}/pyproject.toml +1 -1
  3. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/basics.py +14 -2
  4. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/loggers.py +22 -12
  5. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restarts/state_resolvers.py +1 -1
  6. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restorers/signal.py +6 -1
  7. {qubx-0.6.10 → qubx-0.6.12}/README.md +0 -0
  8. {qubx-0.6.10 → qubx-0.6.12}/build.py +0 -0
  9. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/__init__.py +0 -0
  10. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/_nb_magic.py +0 -0
  11. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/__init__.py +0 -0
  12. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/account.py +0 -0
  13. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/broker.py +0 -0
  14. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/data.py +0 -0
  15. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/management.py +0 -0
  16. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/ome.py +0 -0
  17. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/optimization.py +0 -0
  18. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/runner.py +0 -0
  19. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/simulated_data.py +0 -0
  20. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/simulator.py +0 -0
  21. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/backtester/utils.py +0 -0
  22. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/cli/__init__.py +0 -0
  23. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/cli/commands.py +0 -0
  24. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/cli/deploy.py +0 -0
  25. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/cli/misc.py +0 -0
  26. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/cli/release.py +0 -0
  27. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/connectors/ccxt/__init__.py +0 -0
  28. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/connectors/ccxt/account.py +0 -0
  29. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/connectors/ccxt/broker.py +0 -0
  30. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/connectors/ccxt/customizations.py +0 -0
  31. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/connectors/ccxt/data.py +0 -0
  32. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  33. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/connectors/ccxt/factory.py +0 -0
  34. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/connectors/ccxt/reader.py +0 -0
  35. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/connectors/ccxt/utils.py +0 -0
  36. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/__init__.py +0 -0
  37. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/account.py +0 -0
  38. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/context.py +0 -0
  39. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/exceptions.py +0 -0
  40. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/helpers.py +0 -0
  41. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/initializer.py +0 -0
  42. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/interfaces.py +0 -0
  43. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/lookups.py +0 -0
  44. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/metrics.py +0 -0
  45. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/mixins/__init__.py +0 -0
  46. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/mixins/market.py +0 -0
  47. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/mixins/processing.py +0 -0
  48. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/mixins/subscription.py +0 -0
  49. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/mixins/trading.py +0 -0
  50. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/mixins/universe.py +0 -0
  51. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/series.pxd +0 -0
  52. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/series.pyi +0 -0
  53. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/series.pyx +0 -0
  54. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/utils.pyi +0 -0
  55. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/core/utils.pyx +0 -0
  56. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/data/__init__.py +0 -0
  57. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/data/composite.py +0 -0
  58. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/data/helpers.py +0 -0
  59. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/data/hft.py +0 -0
  60. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/data/readers.py +0 -0
  61. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/data/registry.py +0 -0
  62. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/data/tardis.py +0 -0
  63. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/emitters/__init__.py +0 -0
  64. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/emitters/base.py +0 -0
  65. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/emitters/composite.py +0 -0
  66. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/emitters/prometheus.py +0 -0
  67. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/emitters/questdb.py +0 -0
  68. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/exporters/__init__.py +0 -0
  69. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/exporters/composite.py +0 -0
  70. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/exporters/formatters/__init__.py +0 -0
  71. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/exporters/formatters/base.py +0 -0
  72. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/exporters/formatters/incremental.py +0 -0
  73. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/exporters/formatters/slack.py +0 -0
  74. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/exporters/redis_streams.py +0 -0
  75. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/exporters/slack.py +0 -0
  76. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/features/__init__.py +0 -0
  77. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/features/core.py +0 -0
  78. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/features/orderbook.py +0 -0
  79. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/features/price.py +0 -0
  80. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/features/trades.py +0 -0
  81. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/features/utils.py +0 -0
  82. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/gathering/simplest.py +0 -0
  83. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/math/__init__.py +0 -0
  84. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/math/stats.py +0 -0
  85. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/notifications/__init__.py +0 -0
  86. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/notifications/composite.py +0 -0
  87. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/notifications/slack.py +0 -0
  88. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/pandaz/__init__.py +0 -0
  89. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/pandaz/ta.py +0 -0
  90. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/pandaz/utils.py +0 -0
  91. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/resources/_build.py +0 -0
  92. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/resources/instruments/symbols-binance.cm.json +0 -0
  93. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/resources/instruments/symbols-binance.json +0 -0
  94. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/resources/instruments/symbols-binance.um.json +0 -0
  95. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/resources/instruments/symbols-bitfinex.f.json +0 -0
  96. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/resources/instruments/symbols-bitfinex.json +0 -0
  97. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/resources/instruments/symbols-kraken.f.json +0 -0
  98. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/resources/instruments/symbols-kraken.json +0 -0
  99. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restarts/__init__.py +0 -0
  100. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restarts/time_finders.py +0 -0
  101. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restorers/__init__.py +0 -0
  102. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restorers/balance.py +0 -0
  103. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restorers/factory.py +0 -0
  104. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restorers/interfaces.py +0 -0
  105. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restorers/position.py +0 -0
  106. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restorers/state.py +0 -0
  107. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/restorers/utils.py +0 -0
  108. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/ta/__init__.py +0 -0
  109. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/ta/indicators.pxd +0 -0
  110. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/ta/indicators.pyi +0 -0
  111. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/ta/indicators.pyx +0 -0
  112. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/trackers/__init__.py +0 -0
  113. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/trackers/advanced.py +0 -0
  114. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/trackers/composite.py +0 -0
  115. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/trackers/rebalancers.py +0 -0
  116. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/trackers/riskctrl.py +0 -0
  117. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/trackers/sizers.py +0 -0
  118. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/__init__.py +0 -0
  119. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/_pyxreloader.py +0 -0
  120. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/charting/lookinglass.py +0 -0
  121. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  122. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/marketdata/binance.py +0 -0
  123. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/marketdata/ccxt.py +0 -0
  124. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/marketdata/dukas.py +0 -0
  125. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/misc.py +0 -0
  126. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/ntp.py +0 -0
  127. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/numbers_utils.py +0 -0
  128. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/orderbook.py +0 -0
  129. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/plotting/__init__.py +0 -0
  130. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/plotting/dashboard.py +0 -0
  131. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/plotting/data.py +0 -0
  132. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/plotting/interfaces.py +0 -0
  133. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  134. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  135. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/runner/__init__.py +0 -0
  136. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
  137. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/runner/accounts.py +0 -0
  138. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/runner/configs.py +0 -0
  139. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/runner/runner.py +0 -0
  140. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/time.py +0 -0
  141. {qubx-0.6.10 → qubx-0.6.12}/src/qubx/utils/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: Qubx
3
- Version: 0.6.10
3
+ Version: 0.6.12
4
4
  Summary: Qubx - Quantitative Trading Framework
5
5
  Author: Dmitry Marienko
6
6
  Author-email: dmitry.marienko@xlydian.com
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "Qubx"
7
- version = "0.6.10"
7
+ version = "0.6.12"
8
8
  description = "Qubx - Quantitative Trading Framework"
9
9
  authors = [ "Dmitry Marienko <dmitry.marienko@xlydian.com>", "Yuriy Arabskyy <yuriy.arabskyy@xlydian.com>",]
10
10
  readme = "README.md"
@@ -475,6 +475,7 @@ class Position:
475
475
  self.quantity = quantity
476
476
  self.position_avg_price = pos_average_price
477
477
  self.r_pnl = r_pnl
478
+ self.__pos_incr_qty = abs(quantity)
478
479
 
479
480
  def reset(self) -> None:
480
481
  """
@@ -539,6 +540,7 @@ class Position:
539
540
  deal_pnl = 0
540
541
  quantity = self.quantity
541
542
  comms = 0
543
+ # logger.info(f"{self.instrument.symbol} exec_price={exec_price} fee_amount={fee_amount} position={position}")
542
544
 
543
545
  if quantity != position:
544
546
  pos_change = position - quantity
@@ -550,13 +552,17 @@ class Position:
550
552
  qty_opening = pos_change if prev_direction == direction else pos_change - qty_closing
551
553
 
552
554
  # - extract realized part of PnL
553
- if qty_closing != 0:
555
+ if not np.isclose(qty_closing, 0.0):
554
556
  _abs_qty_close = abs(qty_closing)
555
557
  deal_pnl = qty_closing * (self.position_avg_price - exec_price)
556
558
 
557
559
  quantity += qty_closing
558
560
  self.__pos_incr_qty -= _abs_qty_close
559
561
 
562
+ # logger.info(
563
+ # f"{self.instrument.symbol} qty_closing={qty_closing} deal_pnl={deal_pnl} quantity={quantity} pos_incr_qty={self.__pos_incr_qty} position_avg_price={self.position_avg_price}"
564
+ # )
565
+
560
566
  # - reset average price to 0 if smaller than minimal price change to avoid cumulative error
561
567
  if abs(quantity) < self.instrument.lot_size:
562
568
  quantity = 0.0
@@ -564,11 +570,17 @@ class Position:
564
570
  self.__pos_incr_qty = 0
565
571
 
566
572
  # - if it has something to add to position let's update price and cost
567
- if qty_opening != 0:
573
+ if not np.isclose(qty_opening, 0.0):
568
574
  _abs_qty_open = abs(qty_opening)
575
+
576
+ # logger.info(
577
+ # f"{self.instrument.symbol} qty_opening={qty_opening} exec_price={exec_price} pos_incr_qty={self.__pos_incr_qty} position_avg_price={self.position_avg_price}"
578
+ # )
579
+
569
580
  pos_avg_price_raw = (_abs_qty_open * exec_price + self.__pos_incr_qty * self.position_avg_price) / (
570
581
  self.__pos_incr_qty + _abs_qty_open
571
582
  )
583
+
572
584
  # - round position average price to be in line with how it's calculated by broker
573
585
  self.position_avg_price = self.instrument.round_price_down(pos_avg_price_raw)
574
586
  self.__pos_incr_qty += _abs_qty_open
@@ -216,8 +216,10 @@ class _BaseIntervalDumper:
216
216
  def store(self, timestamp: np.datetime64):
217
217
  _t_ns = time_as_nsec(timestamp)
218
218
  if self._freq:
219
- _interval_start_time = int(_t_ns - _t_ns % self._freq)
220
- if _t_ns - self._last_log_time_ns >= self._freq:
219
+ # Convert freq to nanoseconds for calculation
220
+ _freq_ns = self._freq.astype("int64")
221
+ _interval_start_time = int(_t_ns - (_t_ns % _freq_ns))
222
+ if _t_ns - self._last_log_time_ns >= _freq_ns:
221
223
  self.dump(np.datetime64(_interval_start_time, "ns"), timestamp)
222
224
  self._last_log_time_ns = _interval_start_time
223
225
  else:
@@ -412,22 +414,29 @@ class SignalsLogger(_BaseIntervalDumper):
412
414
 
413
415
  class BalanceLogger(_BaseIntervalDumper):
414
416
  """
415
- Balance logger - send balance on strategy start
417
+ Balance logger - save balance information at regular intervals similar to positions
416
418
  """
417
419
 
418
420
  _writer: LogsWriter
421
+ _balance: dict[str, AssetBalance]
419
422
 
420
- def __init__(self, writer: LogsWriter) -> None:
421
- super().__init__(None) # no intervals
423
+ def __init__(self, writer: LogsWriter, interval: str) -> None:
424
+ super().__init__(interval)
422
425
  self._writer = writer
426
+ self._balance = {}
423
427
 
424
- def record_balance(self, timestamp: np.datetime64, balance: Dict[str, AssetBalance]):
428
+ def record_balance(self, timestamp: np.datetime64, balance: dict[str, AssetBalance]):
425
429
  if balance:
430
+ self._balance = balance
431
+ self.dump(timestamp, timestamp)
432
+
433
+ def dump(self, interval_start_time: np.datetime64, actual_timestamp: np.datetime64):
434
+ if self._balance:
426
435
  data = []
427
- for s, d in balance.items():
436
+ for s, d in self._balance.items():
428
437
  data.append(
429
438
  {
430
- "timestamp": timestamp,
439
+ "timestamp": str(interval_start_time),
431
440
  "currency": s,
432
441
  "total": d.total,
433
442
  "locked": d.locked,
@@ -435,9 +444,6 @@ class BalanceLogger(_BaseIntervalDumper):
435
444
  )
436
445
  self._writer.write_data("balance", data)
437
446
 
438
- def store(self, timestamp: np.datetime64):
439
- pass
440
-
441
447
  def close(self):
442
448
  self._writer.flush_data()
443
449
 
@@ -484,7 +490,7 @@ class StrategyLogging:
484
490
  self.signals_logger = SignalsLogger(logs_writer, num_signals_records_to_write)
485
491
 
486
492
  # - balance logger
487
- self.balance_logger = BalanceLogger(logs_writer)
493
+ self.balance_logger = BalanceLogger(logs_writer, positions_log_freq)
488
494
  else:
489
495
  logger.warning("Log writer is not defined - strategy activity will not be saved !")
490
496
 
@@ -534,6 +540,10 @@ class StrategyLogging:
534
540
  if self.portfolio_logger:
535
541
  self.portfolio_logger.store(timestamp)
536
542
 
543
+ # - notify balance logger
544
+ if self.balance_logger:
545
+ self.balance_logger.store(timestamp)
546
+
537
547
  # - log heartbeat
538
548
  self._log_heartbeat(timestamp)
539
549
 
@@ -43,7 +43,7 @@ class StateResolver:
43
43
  ctx.trade(instrument, -live_qty)
44
44
 
45
45
  # If live position is larger than sim position (same direction), reduce it
46
- elif abs(live_qty) > abs(sim_qty) and abs(sim_qty) > instrument.lot_size:
46
+ elif abs(live_qty) > abs(sim_qty) and abs(live_qty) > instrument.lot_size:
47
47
  qty_diff = sim_qty - live_qty
48
48
  logger.info(
49
49
  f"Reducing position for {instrument.symbol}: {live_qty} -> {sim_qty} (diff: {qty_diff})"
@@ -78,7 +78,12 @@ class CsvSignalRestorer(ISignalRestorer):
78
78
 
79
79
  try:
80
80
  # Read the CSV file
81
- df = pd.read_csv(file_path)
81
+ try:
82
+ df = pd.read_csv(file_path)
83
+ except Exception as e:
84
+ logger.info(f"Could not read signal file {file_path}: {e}")
85
+ return {}
86
+
82
87
  if df.empty:
83
88
  logger.info(f"No signals found in {file_path}")
84
89
  return {}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes