Qubx 0.1.8__cp311-cp311-manylinux_2_35_x86_64.whl → 0.1.82__cp311-cp311-manylinux_2_35_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.

qubx/data/readers.py CHANGED
@@ -503,56 +503,27 @@ def _retry(fn):
503
503
  return wrapper
504
504
 
505
505
 
506
- class QuestDBConnector(DataReader):
506
+ class QuestDBSqlBuilder:
507
507
  """
508
- Very first version of QuestDB connector
509
-
510
- ### Connect to an existing QuestDB instance
511
- >>> db = QuestDBConnector()
512
- >>> db.read('BINANCE.UM:ETHUSDT', '2024-01-01', transform=AsPandasFrame())
508
+ Generic sql builder for QuestDB data
513
509
  """
514
- _reconnect_tries = 5
515
- _reconnect_idle = 0.1 # wait seconds before retying
516
510
 
517
- def __init__(self, host='localhost', user='admin', password='quest', port=8812) -> None:
518
- self._connection = None
519
- self._cursor = None
520
- self._host = host
521
- self._port = port
522
- self.connection_url = f'user={user} password={password} host={host} port={port}'
523
- self._connect()
524
-
525
- def _connect(self):
526
- self._connection = pg.connect(self.connection_url, autocommit=True)
527
- self._cursor = self._connection.cursor()
528
- logger.debug(f"Connected to QuestDB at {self._host}:{self._port}")
511
+ def get_table_name(self, data_id: str, sfx: str='') -> str | None:
512
+ pass
529
513
 
530
- @_retry
531
- def read(self, data_id: str, start: str|None=None, stop: str|None=None,
532
- transform: DataTransformer = DataTransformer(),
533
- chunksize=0, # TODO: use self._cursor.fetchmany in this case !!!!
534
- timeframe: str='1m') -> Any:
535
- start, end = handle_start_stop(start, stop)
536
- _req = self._prepare_data_sql(data_id, start, end, timeframe)
514
+ def prepare_data_sql(self, data_id: str, start: str|None, end: str|None, resample: str, suffix: str) -> str | None:
515
+ pass
537
516
 
538
- self._cursor.execute(_req) # type: ignore
539
- records = self._cursor.fetchall() # TODO: for chunksize > 0 use fetchmany etc
540
-
541
- names = [d.name for d in self._cursor.description] # type: ignore
542
- transform.start_transform(data_id, names)
517
+ def prepare_names_sql(self) -> str:
518
+ return "select table_name from tables()"
543
519
 
544
- transform.process_data(records)
545
- return transform.collect()
546
520
 
547
- @staticmethod
548
- def _convert_time_delta_to_qdb_resample_format(c_tf: str):
549
- if c_tf:
550
- _t = re.match(r'(\d+)(\w+)', c_tf)
551
- if _t and len(_t.groups()) > 1:
552
- c_tf = f"{_t[1]}{_t[2][0].lower()}"
553
- return c_tf
521
+ class QuestDBSqlCandlesBuilder(QuestDBSqlBuilder):
522
+ """
523
+ Sql builder for candles data
524
+ """
554
525
 
555
- def _get_table_name(self, data_id: str, sfx: str='') -> str:
526
+ def get_table_name(self, data_id: str, sfx: str='') -> str:
556
527
  """
557
528
  Get table name for data_id
558
529
  data_id can have format <exchange>.<type>:<symbol>
@@ -573,7 +544,15 @@ class QuestDBConnector(DataReader):
573
544
  table_name = '.'.join(filter(lambda x: x, [_exch.lower(), _aliases.get(_mktype, _mktype), symb.lower(), sfx]))
574
545
  return table_name
575
546
 
576
- def _prepare_data_sql(self, data_id: str, start: str|None, end: str|None, resample: str) -> str:
547
+ @staticmethod
548
+ def _convert_time_delta_to_qdb_resample_format(c_tf: str):
549
+ if c_tf:
550
+ _t = re.match(r'(\d+)(\w+)', c_tf)
551
+ if _t and len(_t.groups()) > 1:
552
+ c_tf = f"{_t[1]}{_t[2][0].lower()}"
553
+ return c_tf
554
+
555
+ def prepare_data_sql(self, data_id: str, start: str|None, end: str|None, resample: str, suffix: str) -> str:
577
556
  where = ''
578
557
  w0 = f"timestamp >= '{start}'" if start else ''
579
558
  w1 = f"timestamp <= '{end}'" if end else ''
@@ -583,10 +562,10 @@ class QuestDBConnector(DataReader):
583
562
  where = f'where {w0} and {w1}' if (w0 and w1) else f"where {(w0 or w1)}"
584
563
 
585
564
  # - check resample format
586
- resample = QuestDBConnector._convert_time_delta_to_qdb_resample_format(resample) if resample else resample
565
+ resample = QuestDBSqlCandlesBuilder._convert_time_delta_to_qdb_resample_format(resample) if resample else resample
587
566
  _rsmpl = f"SAMPLE by {resample}" if resample else ''
588
567
 
589
- table_name = self._get_table_name(data_id, 'candles_1m')
568
+ table_name = self.get_table_name(data_id, suffix)
590
569
  return f"""
591
570
  select timestamp,
592
571
  first(open) as open,
@@ -601,12 +580,54 @@ class QuestDBConnector(DataReader):
601
580
  from "{table_name}" {where} {_rsmpl};
602
581
  """
603
582
 
604
- def _prepare_names_sql(self) -> str:
605
- return "select table_name from tables()"
583
+
584
+ class QuestDBConnector(DataReader):
585
+ """
586
+ Very first version of QuestDB connector
587
+
588
+ ### Connect to an existing QuestDB instance
589
+ >>> db = QuestDBConnector()
590
+ >>> db.read('BINANCE.UM:ETHUSDT', '2024-01-01', transform=AsPandasFrame())
591
+ """
592
+ _reconnect_tries = 5
593
+ _reconnect_idle = 0.1 # wait seconds before retying
594
+ _builder: QuestDBSqlBuilder
595
+
596
+ def __init__(self, builder: QuestDBSqlBuilder = QuestDBSqlCandlesBuilder(),
597
+ host='localhost', user='admin', password='quest', port=8812) -> None:
598
+ self._connection = None
599
+ self._cursor = None
600
+ self._host = host
601
+ self._port = port
602
+ self.connection_url = f'user={user} password={password} host={host} port={port}'
603
+ self._builder = builder
604
+ self._connect()
605
+
606
+ def _connect(self):
607
+ self._connection = pg.connect(self.connection_url, autocommit=True)
608
+ self._cursor = self._connection.cursor()
609
+ logger.debug(f"Connected to QuestDB at {self._host}:{self._port}")
610
+
611
+ @_retry
612
+ def read(self, data_id: str, start: str|None=None, stop: str|None=None,
613
+ transform: DataTransformer = DataTransformer(),
614
+ chunksize=0, # TODO: use self._cursor.fetchmany in this case !!!!
615
+ timeframe: str='1m', suffix='candles_1m') -> Any:
616
+ start, end = handle_start_stop(start, stop)
617
+ _req = self._builder.prepare_data_sql(data_id, start, end, timeframe, suffix)
618
+
619
+ self._cursor.execute(_req) # type: ignore
620
+ records = self._cursor.fetchall() # TODO: for chunksize > 0 use fetchmany etc
621
+
622
+ names = [d.name for d in self._cursor.description] # type: ignore
623
+ transform.start_transform(data_id, names)
624
+
625
+ transform.process_data(records)
626
+ return transform.collect()
606
627
 
607
628
  @_retry
608
629
  def get_names(self) -> List[str] :
609
- self._cursor.execute(self._prepare_names_sql()) # type: ignore
630
+ self._cursor.execute(self._builder.prepare_names_sql()) # type: ignore
610
631
  records = self._cursor.fetchall()
611
632
  return [r[0] for r in records]
612
633
 
@@ -652,14 +673,23 @@ class SnapshotsBuilder(DataTransformer):
652
673
  return self.buffer
653
674
 
654
675
 
676
+ class QuestDBSqlOrderBookBilder(QuestDBSqlBuilder):
677
+ """
678
+ Sql builder for snapshot data
679
+ """
680
+
681
+ def get_table_name(self, data_id: str, sfx: str='') -> str:
682
+ return ''
683
+
684
+ def prepare_data_sql(self, data_id: str, start: str|None, end: str|None, resample: str, suffix: str) -> str:
685
+ return ''
686
+
687
+
655
688
  class QuestDBOrderBookConnector(QuestDBConnector):
656
689
  """
657
690
  Example of custom OrderBook data connector
658
691
  """
659
692
 
660
- def _prepare_data_sql(self, data_id: str, start: str|None, end: str|None, resample: str|None) -> str:
661
- raise NotImplemented("TODO")
662
-
663
- def _prepare_names_sql(self) -> str:
664
- # return "select table_name from tables() where ..."
665
- raise NotImplemented("TODO")
693
+ def __init__(self, host='localhost', user='admin', password='quest', port=8812) -> None:
694
+ super().__init__(QuestDBSqlOrderBookBilder(), host, user, password, port)
695
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Qubx
3
- Version: 0.1.8
3
+ Version: 0.1.82
4
4
  Summary: Qubx - quantitative trading framework
5
5
  Home-page: https://github.com/dmarienko/Qubx
6
6
  Author: Dmitry Marienko
@@ -6,13 +6,13 @@ qubx/core/basics.py,sha256=2u7WV5KX-RbTmzoKfi1yT4HNLDPfQcFMCUZ1pVsM_VE,14777
6
6
  qubx/core/helpers.py,sha256=gPE78dO718NBY0-JbfqNGCzIvr4BVatFntNIy2RUrEY,11559
7
7
  qubx/core/loggers.py,sha256=HpgavBZegoDv9ssihtqX0pitXKULVAPHUpoE_volJw0,11910
8
8
  qubx/core/lookups.py,sha256=4aEC7b2AyEXFqHHGDenex3Z1FZGrpDSb8IwzBZrSqIA,13688
9
- qubx/core/series.cpython-311-x86_64-linux-gnu.so,sha256=ltVxtoC4f0B4skXk6yRtoryDzon6gwLX_3NuX7gb5io,698952
9
+ qubx/core/series.cpython-311-x86_64-linux-gnu.so,sha256=FKD447WQCDiib-wBliVN0amtMroO_iEvVnRsg22n73U,698952
10
10
  qubx/core/series.pxd,sha256=IS89NQ5FYp3T0YIHe1lELKZIAKrNvX8K6WlLyac44I4,2847
11
11
  qubx/core/series.pyx,sha256=WEAjn4j3zn540Cxx68X5gRXilvwa7NGdbki6myzZbIM,28108
12
12
  qubx/core/strategy.py,sha256=Fs4fFyHaEGYuz7mBeQHBWFu3Ipg0yFzcxXhskgsPxJE,30330
13
- qubx/core/utils.cpython-311-x86_64-linux-gnu.so,sha256=5ddzSg68VpiWMCEtase_w0XhyEb4DUjP7xWxL55XQ4k,74216
13
+ qubx/core/utils.cpython-311-x86_64-linux-gnu.so,sha256=dFR1VwEHg4biWv-vrn9nf-JPKMs3L4-lf_4EXMQFb8I,74216
14
14
  qubx/core/utils.pyx,sha256=6dQ8R02bl8V3f-W3Wk9-e86D9OvDz-5-4NA_dlF_xwc,1368
15
- qubx/data/readers.py,sha256=EuYLt7mmf4TPtVzlbbxGvgw-luR7oDPwdLJQe37iU6c,24786
15
+ qubx/data/readers.py,sha256=HDUoXNjpObtbfxrMW3PB4ypyB7WvJUvCg2Ulww4NChs,25600
16
16
  qubx/impl/ccxt_connector.py,sha256=NqF-tgxfTATnmVqKUonNXCAzECrDU8YrgqM3Nq06fw8,9150
17
17
  qubx/impl/ccxt_customizations.py,sha256=kK_4KmOyKvDVgd4MTkVg4CyqdjE-6r41siZIvLj-A-Q,3488
18
18
  qubx/impl/ccxt_trading.py,sha256=cmg4P-zd78w-V8j3-IGS2LFxikGhxFPgmCvz3sr065Q,9097
@@ -23,7 +23,7 @@ qubx/pandaz/__init__.py,sha256=Iw5uzicYGSC3FEKZ-W1O5-7cXq_P0kH11-EcXV0zZhs,175
23
23
  qubx/pandaz/ta.py,sha256=TUvjrvmk4EQvDcXoRp6Os08-HUap-ZvpSDGawhViOgg,85271
24
24
  qubx/pandaz/utils.py,sha256=FyLKQy8spkqxhBij_nPFC_ZzI_L3-IgB9O53MqWKmq0,19109
25
25
  qubx/ta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
- qubx/ta/indicators.cpython-311-x86_64-linux-gnu.so,sha256=JDkaQwaNS6-J9ICrxJsATcSxaZKvURf0jtQ-09vhUGg,284552
26
+ qubx/ta/indicators.cpython-311-x86_64-linux-gnu.so,sha256=v9kiNK0VWcOhllotwWfPPZ3De2CLpWlvDbVSUxqeuHQ,284552
27
27
  qubx/ta/indicators.pyx,sha256=P-GEYUks2lSHo6hbtUFAB7TWE1AunjLR4jIjwqPHrwU,7708
28
28
  qubx/trackers/__init__.py,sha256=1y_yvIy0OQwBqfhAW_EY33NxFzFSWvI0qNAPU6zchYc,60
29
29
  qubx/trackers/rebalancers.py,sha256=QCzANCooZBi2VMCBjjCPMq_Dt1h1zrBelATnfmVve74,5522
@@ -34,6 +34,6 @@ qubx/utils/marketdata/binance.py,sha256=36dl4rxOAGTeY3uoONmiPanj8BkP0oBdDiH-URJJ
34
34
  qubx/utils/misc.py,sha256=z5rdz5hbRu9-F2QgF47OCkMvhfIkRKs-PHR8L5DWkBM,9831
35
35
  qubx/utils/runner.py,sha256=OY7SoRfxHwzn0rKTGB_lbg5zNASEL_49hQXWqs-LiMk,9306
36
36
  qubx/utils/time.py,sha256=_DjCdQditzZwMy_8rvPdWyw5tjw__2p24LMPgXdZ8i0,4911
37
- qubx-0.1.8.dist-info/METADATA,sha256=W62rM-gCLkzeF0GQ087NFox6vZ6wQBouO_aSfRaDoV8,2490
38
- qubx-0.1.8.dist-info/WHEEL,sha256=MLOa6LysROdjgj4FVxsHitAnIh8Be2D_c9ZSBHKrz2M,110
39
- qubx-0.1.8.dist-info/RECORD,,
37
+ qubx-0.1.82.dist-info/METADATA,sha256=MXKLJx9nRdbg1BE5emc8ovNDC7HoB78N0BYhWKR5Wfk,2491
38
+ qubx-0.1.82.dist-info/WHEEL,sha256=MLOa6LysROdjgj4FVxsHitAnIh8Be2D_c9ZSBHKrz2M,110
39
+ qubx-0.1.82.dist-info/RECORD,,
File without changes