GithubHelp home page GithubHelp logo

alpacahq / pymarketstore Goto Github PK

View Code? Open in Web Editor NEW
103.0 38.0 40.0 118 KB

Python driver for MarketStore

License: Apache License 2.0

Makefile 0.75% Python 99.25%
marketstore pip pandas pandas-dataframe analytics timeseries quantitative-finance alpaca python3

pymarketstore's Introduction

pymarketstore

Python driver for MarketStore

Build Status: build status

Pymarketstore can query and write financial timeseries data from MarketStore

Tested with 3.3+

How to install

$ pip install pymarketstore

Examples

In [1]: import pymarketstore as pymkts

## query data

In [2]: param = pymkts.Params('BTC', '1Min', 'OHLCV', limit=10)

In [3]: cli = pymkts.Client()

In [4]: reply = cli.query(param)

In [5]: reply.first().df()
Out[5]:
                               Open      High       Low     Close     Volume
Epoch
2018-01-17 17:19:00+00:00  10400.00  10400.25  10315.00  10337.25   7.772154
2018-01-17 17:20:00+00:00  10328.22  10359.00  10328.22  10337.00  14.206040
2018-01-17 17:21:00+00:00  10337.01  10337.01  10180.01  10192.15   7.906481
2018-01-17 17:22:00+00:00  10199.99  10200.00  10129.88  10160.08  28.119562
2018-01-17 17:23:00+00:00  10140.01  10161.00  10115.00  10115.01  11.283704
2018-01-17 17:24:00+00:00  10115.00  10194.99  10102.35  10194.99  10.617131
2018-01-17 17:25:00+00:00  10194.99  10240.00  10194.98  10220.00   8.586766
2018-01-17 17:26:00+00:00  10210.02  10210.02  10101.00  10138.00   6.616969
2018-01-17 17:27:00+00:00  10137.99  10138.00  10108.76  10124.94   9.962978
2018-01-17 17:28:00+00:00  10124.95  10142.39  10124.94  10142.39   2.262249

## write data

In [7]: import numpy as np

In [8]: import pandas as pd

In [9]: data = np.array([(pd.Timestamp('2017-01-01 00:00').value / 10**9, 10.0)], dtype=[('Epoch', 'i8'), ('Ask', 'f4')])

In [10]: cli.write(data, 'TEST/1Min/Tick')
Out[10]: {'responses': None}

In [11]: cli.query(pymkts.Params('TEST', '1Min', 'Tick')).first().df()
Out[11]:
                            Ask
Epoch
2017-01-01 00:00:00+00:00  10.0

Client

pymkts.Client(endpoint='http://localhost:5993/rpc')

Construct a client object with endpoint.

Query

pymkts.Client#query(symbols, timeframe, attrgroup, start=None, end=None, limit=None, limit_from_start=False)

You can build parameters using pymkts.Params.

  • symbols: string for a single symbol or a list of symbol string for multi-symbol query
  • timeframe: timeframe string
  • attrgroup: attribute group string. symbols, timeframe and attrgroup compose a bucket key to query in the server
  • start: unix epoch second (int), datetime object or timestamp string. The result will include only data timestamped equal to or after this time.
  • end: unix epoch second (int), datetime object or timestamp string. The result will include only data timestamped equal to or before this time.
  • limit: the number of records to be returned, counting from either start or end boundary.
  • limit_from_start: boolean to indicate limit is from the start boundary. Defaults to False.

Pass one or multiple instances of Params to Client.query(). It will return QueryReply object which holds internal numpy array data returned from the server.

Write

pymkts.Client#write(data, tbk)

You can write a numpy array to the server via Client.write() method. The data parameter must be numpy's recarray type with a column named Epoch in int64 type at the first column. tbk is the bucket key of the data records.

List Symbols

pymkts.Client#list_symbols()

The list of all symbols stored in the server are returned.

Server version

pymkts.Client#server_version()

Returns a string of Marketstore-Version header from a server response.

Streaming

If the server supports WebSocket streaming, you can connect to it using pymkts.StreamConn class. For convenience, you can call pymkts.Client#stream() to obtain the instance with the same server information as REST client.

Once you have this instance, you will set up some event handles by either register() method or @on() decorator. These methods accept regular expressions to filter which stream to act on.

To actually connect and start receiving the messages from the server, you will call run() with the stream names. By default, it subscribes to all by */*/*.

pymkts.Client#stream()

Return a StreamConn which is a websocket connection to the server.

pymkts.StreamConn#(endpoint)

Create a connection instance to the endpoint server. The endpoint string is a full URL with "ws" or "wss" scheme with the port and path.

pymkts.StreamConn#register(stream_path, func) @pymkts.StreamConn#on(stream_path)

Add a new message handler to the connection. The function will be called with handler(StreamConn, {"key": "...", "data": {...,}}) if the key (time bucket key) matches with the stream_path regular expression. The on method is a decorator version of register.

pymkts.StreamConn#run([stream1, stream2, ...])

Start communication with the server and go into an indefinite loop. It does not return until unhandled exception is raised, in which case the connection is closed so you need to implement retry. Also, since this is a blocking method, you may need to run it in a background thread.

An example code is as follows.

import pymarketstore as pymkts

conn = pymkts.StreamConn('ws://localhost:5993/ws')

@conn.on(r'^BTC/')
def on_btc(conn, msg):
    print('received btc', msg['data'])

conn.run(['BTC/*/*'])  # runs until exception

-> received btc {'Open': 4370.0, 'High': 4372.93, 'Low': 4370.0, 'Close': 4371.74, 'Volume': 3.3880948699999993, 'Epoch': 1507299600}

pymarketstore's People

Contributors

briancappello avatar bynr avatar dakimura avatar rocketbitz avatar umitanuki avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pymarketstore's Issues

Add python version informations

The use of six for iterators should make it work for python 2 and 3, but it can be helpful to directly put in the readme the supported python versions.
Incidentally, maybe explain the intended handling of end of support for python 2 as two years ahead in not so far?

BUG: Requirements reference outdated `msgpack-python` name on PyPI

Current behaviour

Current setup.py defines msgpack-python.
ref:

'msgpack-python',

However, the latest name of msgpack on PyPI is msgpack:

Package name on PyPI was changed from msgpack-python to msgpack from 0.5.
ref: https://pypi.org/project/msgpack/

The issue is that a user installing pymarketstore cannot also use the latest version of msgpack, and is stuck with using msgpack up to version 0.5.6 whereas the latest one is 1.0.2.

Proposed fix

Update the requirements in

'msgpack-python',

to rename msgpack-python to msgpack

can not install via pip

% pip install pymarketstore
Collecting pymarketstore
  Using cached pymarketstore-0.9.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-3tt4f246/pymarketstore/setup.py", line 3, in <module>
        from pymarketstore import __version__
      File "/tmp/pip-build-3tt4f246/pymarketstore/pymarketstore/__init__.py", line 1, in <module>
        from .client import Client, Params  # noqa
      File "/tmp/pip-build-3tt4f246/pymarketstore/pymarketstore/client.py", line 1, in <module>
        import numpy as np
    ModuleNotFoundError: No module named 'numpy'

This is caused because import Client in pymarketstore/__init__.py.

Is anyone interested in an async client?

I've already got one underway in one of my projects and it avoids a lot of cruft I've found in this client.

Since it seems there isn't much activity in terms of maintainer feedback here how many peeps would be interested in this published as a 3rd party project?


UPDATE:

We have an alpha grade one underway @ https://github.com/pikers/anyio-marketstore

Please provide feedback on that repo ๐Ÿ˜Ž

Cannot stream and write concurrently

The Issue

When streaming from marketstore, any attempts to write to the server results in a Could not contact server error.

The Workaround(s)

  • Running multiple apps, one for streaming and one for writing works, but not ideal for writing data that depends on the stream.
  • Duplicating the package into pymarketstore-stream and pymarketstore-write packages would circumvent this entirely. This is just a workaround though, and hopefully this will be addressed in time. Possibly via a write function through the websocket? (I attempted this but was unsuccessful)

Writing lower case numpy dtype field names is a "nono"

Got bit by this and documented it in alpacahq/marketstore#324. I feel that writing a test would be handy for tracking as well as maybe some protection client-side to avoid users hitting this.

2 questions:

  • should the client maybe do better error reporting from the server? Right now this is being deferred to user client code. I wonder if a flag like Client(raise_response_errors=True) would be handy?
  • There should be something in the docs stating that you can't use lower case structarray field names or even make Client.write() check for capital field names? This situation makes me think that the Client.create() approach (from #37) would allow for this without having to check in every .write() call.

pandas not in dependency

>>> import pymarketstore as pymkts
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/fooo/tmp/pymarketstore/pymarketstore/__init__.py", line 1, in <module>
    from .client import Client, Params  # noqa
  File "/home/fooo/tmp/pymarketstore/pymarketstore/client.py", line 2, in <module>
    import pandas as pd
ModuleNotFoundError: No module named 'pandas'

pandas is not in dependency.

Ticks not supported

Observation

Pymarketstore query assumes all asset classes have an Epoch field which is in seconds since Unix time.

index = pd.to_datetime(df.index, unit='s', utc=True)

This is valid for OHLCV data for which the lowest timeframe is 1S, but does not work for TICK data.

Conclusion

Current pymarketstore does not support writing or querying tick data.

The roadmap can be:

  • For querying ticks, if nanoseconds field is present in the returned data, then we should add it to the time index.
    (as in the previous client)
    https://github.com/AlpacaDB/libalpaca/blob/develop/libalpaca/marketstore/client.py#L609

  • For writing ticks, we need to:

    • test if the nanoseconds is actually written
    • is duplicated Epoch supported
    • is duplicated Epoch+nanoseconds supported
    • can we write ticks to the same bucket in multiple times without overriding (ideally, having an override parameter would make things more explicit)

Completely broken on 3.10 due to proto defs/imports

On python 3.10 one can't even import the package:

 File "/home/goodboy/repos/piker/310/lib/python3.10/site-packages/pymarketstore/__init__.py", line 1, in <module>
    from .client import Client  # noqa
  File "/home/goodboy/repos/piker/310/lib/python3.10/site-packages/pymarketstore/client.py", line 9, in <module>
    from .grpc_client import GRPCClient
  File "/home/goodboy/repos/piker/310/lib/python3.10/site-packages/pymarketstore/grpc_client.py", line 7, in <module>
    import pymarketstore.proto.marketstore_pb2 as proto
  File "/home/goodboy/repos/piker/310/lib/python3.10/site-packages/pymarketstore/proto/marketstore_pb2.py", line 17, in <module>
    DESCRIPTOR = _descriptor.FileDescriptor(
  File "/home/goodboy/repos/piker/310/lib/python3.10/site-packages/google/protobuf/descriptor.py", line 982, in __new__
    return _message.default_pool.AddSerializedFile(serialized_pb)
TypeError: Couldn't build proto file into descriptor pool!
Invalid proto descriptor for file "pymarketstore/proto/marketstore.proto":
  proto.DataShape.name: "proto.DataShape.name" is already defined in file "proto/marketstore.proto".
  proto.DataShape.type: "proto.DataShape.type" is already defined in file "proto/marketstore.proto".
  proto.DataShape: "proto.DataShape" is already defined in file "proto/marketstore.proto".
  proto.NumpyMultiDataset.data: "proto.NumpyMultiDataset.data" is already defined in file "proto/marketstore.proto".
  proto.NumpyMultiDataset.start_index: "proto.NumpyMultiDataset.start_index" is already defined in file "proto/marketstore.proto".
  ....
  IT GOES ON AND ON HERE

client.write() does not work with python 2.7

When I try the example in the README, it fails with a corrupted time index:

import numpy as np
import pandas as pd
import pymarketstore as pymkts

data = np.array([(pd.Timestamp('2017-01-01 00:00').value / 10**9, 10.0)], dtype=[('Epoch', 'i8'), ('Ask', 'f4')])

cli = pymkts.Client()

cli.write(data, 'TEST/1Min/Tick')

d2 = cli.query(pymkts.Params('TEST', '1Min', 'Tick')).first().df()
print d2

I get the following output:

# python tests/basic_driver.py 
Empty DataFrame
Columns: [Ask]
Index: []

I believe the problem is in the handling of the numpy data in the driver as compared with the handling in the API code in the marketstore server. To fix this, we should:

  • develop and document the binary data interchange format
  • implement a basic sanity test of the index values used in the data package - perhaps send the beginning and ending values of time index in the intended write data in the data package
  • implement a system level test in CircleCI that verifies this driver's correct operation with the latest marketstore codebase

Get data or save data in correct number

I get data with alpaca-py and save it to marketstore

  1. I get these values but i am not sure if i am writing wrong to marketstore or if the values are correct.
  2. IF values are correct how i can convert them to normal numbers to understand or to show in graphs?

Stock: FDX - Fedex

2023-01-20 14:00:00+00:00 1.674223e+09 1.674223e+09 1.674223e+09 1.674223e+09 1.674223e+09 1.674223e+09 1.674223e+09
2023-01-20 15:00:00+00:00 1.674227e+09 1.674227e+09 1.674227e+09 1.674227e+09 1.674227e+09 1.674227e+09 1.674227e+09

proposal/RFC: pymkts.Client is currently a broken public interface

As it stands right now, the public interface for this library is pymkts.Client. However, all it actually does is proxy calls to either JsonRpcClient or GRPCClient. Which is fine, except for the fact that the return values of the two underlying clients are not always compatible. This breaks the principle of composability. For example:

Client(grpc=False).write() returns {'responses': None} on success and {'responses': [{'error': 'message', 'version': 'v'}] on error.
Meanwhile, Client(grpc=True).write() returns protobuf descriptors (essentially, attribute-access-only dicts).

Therefore, writing code against pymkts.Client will break if you switch between the real underlying clients. IMO that's no good. Currently the only "troublesome" methods are Client.write and Client.destroy, but, it will only get worse as the API grows.

I see four options:

  1. Convert responses from the GRPCClient to be dictionaries compatible with those returned from JsonRpcClient (maintains backwards compatibility, simple but no useful type-hinting or code-completion)
  2. Convert responses from both clients to be attribute-accessable dictionaries[1] (maintains backwards compatibility for both clients, simple but no useful type-hinting or code-completion)
  3. Add basic response data classes[2] and convert the responses from both clients to them. Somewhat less simple, but provides useful type-hinting and code-completion, and done correctly, could maintain backwards compatibilty with both GRPCClient and JsonRpcClient.
  4. Do away with pymkts.Client all together and leave it up to users to pick between from pymarketstore import GRPCClient as Client or from pymarketstore import JsonRpcClient as Client.

Thoughts? I'm happy to refactor the code if it will get merged :)

# [1] attribute-accessible dictionaries
class AttrDict(dict):
    def __getattr__(self, key):
        return self[key]

    def __setattr__(self, key, value):
        self[key] = value

# [2] response data classes (written against the msgpack response format, but you get the idea)
class WriteResponse:
    def __init__(response):
        self.responses = response['responses']

    @property
    def success(self) -> bool:
        return not self.responses

    @property
    def errors(self) -> List[AttrDict]:
        return [AttrDict(r) for r in self.responses]  # or even better, a list of ErrorResponse instances

Connection Error

I have Marketstore running @ localhost:5993, but running into this error.

ConnectionError: HTTPConnectionPool(host='localhost', port=5993): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7ff8844ed1d0>: Failed to establish a new connection: [Errno 111] Connection refused'))

import pymarketstore as pymkts
param = pymkts.Params('BTC', '1Min', 'OHLCV', limit=10)
cli = pymkts.Client(endpoint='http://localhost:5993/rpc')

reply = cli.query(param) <-- This is where the error occurs.

Import Data from Alpaca-py

How you can import data from Alpaca-py ? I don't understand how you can convert the output from alpaca-py and input it to marketstore

stock_request = StockBarsRequest(
                    symbol_or_symbols=["SPY"],
                    timeframe=TimeFrame.Minute,
                    start=datetime.strptime("2022-12-30", '%Y-%m-%d')
                )

bars = stock_client.get_stock_bars(stock_request)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.