mp_fomo_polish: some feat additions, code styling corrections, general fixes #43
|
|
@ -90,6 +90,14 @@ bc why install with `python` when you can faster with `rust` ::
|
||||||
|
|
||||||
uv lock
|
uv lock
|
||||||
|
|
||||||
|
with all GUI support as well::
|
||||||
|
|
||||||
|
uv lock --extra uis
|
||||||
|
|
||||||
|
AND with all dev (hacking) tools::
|
||||||
|
|
||||||
|
uv lock --dev --extra uis
|
||||||
|
|
||||||
|
|
||||||
hacky install on nixos
|
hacky install on nixos
|
||||||
**********************
|
**********************
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,31 @@
|
||||||
from decimal import (
|
from decimal import (
|
||||||
Decimal,
|
Decimal,
|
||||||
)
|
)
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import polars as pl
|
# import polars as pl
|
||||||
import trio
|
import trio
|
||||||
import tractor
|
import tractor
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pprint import pformat
|
# from pprint import pformat
|
||||||
from piker.brokers.deribit.api import (
|
from piker.brokers.deribit.api import (
|
||||||
get_client,
|
get_client,
|
||||||
maybe_open_oi_feed,
|
maybe_open_oi_feed,
|
||||||
)
|
)
|
||||||
from piker.storage import open_storage_client, StorageClient
|
from piker.storage import open_storage_client, StorageClient
|
||||||
|
from piker.log import get_logger
|
||||||
import sys
|
import sys
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from PyQt6 import QtCore
|
from PyQt6 import QtCore
|
||||||
from pyqtgraph import ScatterPlotItem, InfiniteLine
|
from pyqtgraph import ScatterPlotItem, InfiniteLine
|
||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
|
from cryptofeed.symbols import Symbol
|
||||||
|
|
||||||
|
|
||||||
|
log = get_logger(__name__)
|
||||||
|
# XXX, use 2 newlines between top level LOC (even between these
|
||||||
|
# imports and the next function line ;)
|
||||||
|
|
||||||
def check_if_complete(
|
def check_if_complete(
|
||||||
oi: dict[str, dict[str, Decimal | None]]
|
oi: dict[str, dict[str, Decimal | None]]
|
||||||
|
|
@ -45,11 +54,16 @@ async def max_pain_daemon(
|
||||||
kind=kind
|
kind=kind
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f'Available expiration dates for {currency}-{kind}:')
|
log.info(
|
||||||
print(f'{expiry_dates}')
|
f'Available expiries for {currency!r}-{kind}:\n'
|
||||||
expiry_date = input('Please enter a valid expiration date: ').upper()
|
f'{expiry_dates}\n'
|
||||||
|
)
|
||||||
|
expiry_date: str = input(
|
||||||
|
'Please enter a valid expiration date: '
|
||||||
|
).upper()
|
||||||
print('Starting little daemon...')
|
print('Starting little daemon...')
|
||||||
|
|
||||||
|
# maybe move this type annot down to the assignment line?
|
||||||
oi_by_strikes: dict[str, dict[str, Decimal]]
|
oi_by_strikes: dict[str, dict[str, Decimal]]
|
||||||
instruments = await client.get_instruments(
|
instruments = await client.get_instruments(
|
||||||
expiry_date=expiry_date,
|
expiry_date=expiry_date,
|
||||||
|
|
@ -189,10 +203,13 @@ async def max_pain_daemon(
|
||||||
),
|
),
|
||||||
], dtype=dtype)
|
], dtype=dtype)
|
||||||
|
|
||||||
path = await client.write_oi(
|
path: Path = await client.write_oi(
|
||||||
col_sym_key,
|
col_sym_key,
|
||||||
data,
|
data,
|
||||||
)
|
)
|
||||||
|
# TODO, use std logging like this throughout for status
|
||||||
|
# emissions on console!
|
||||||
|
log.info(f'Wrote OI history to {path}')
|
||||||
|
|
||||||
def get_max_pain(
|
def get_max_pain(
|
||||||
oi_by_strikes: dict[str, dict[str, Decimal]]
|
oi_by_strikes: dict[str, dict[str, Decimal]]
|
||||||
|
|
@ -255,11 +272,33 @@ async def max_pain_daemon(
|
||||||
# hardcoded to something, sorry.)
|
# hardcoded to something, sorry.)
|
||||||
timestamp = msg[1]['timestamp']
|
timestamp = msg[1]['timestamp']
|
||||||
max_pain = get_max_pain(oi_by_strikes)
|
max_pain = get_max_pain(oi_by_strikes)
|
||||||
intrinsic_values = get_total_intrinsic_values(oi_by_strikes)
|
# intrinsic_values = get_total_intrinsic_values(oi_by_strikes)
|
||||||
|
|
||||||
# graph here
|
# graph here
|
||||||
plot_graph(oi_by_strikes, plot)
|
plot_graph(oi_by_strikes, plot)
|
||||||
|
|
||||||
|
# TODO, use a single multiline string with `()`
|
||||||
|
# and drop the multiple `print()` calls (this
|
||||||
|
# should be done elsewhere in this file as well!
|
||||||
|
#
|
||||||
|
# As per the docs,
|
||||||
|
# https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation
|
||||||
|
# you could instead do,
|
||||||
|
# print(
|
||||||
|
# '-----------------------------------------------\n'
|
||||||
|
# f'timestamp: {datetime.fromtimestamp(max_pain['timestamp'])}\n'
|
||||||
|
# )
|
||||||
|
# WHY?
|
||||||
|
# |_ less ctx-switches/calls to `print()`
|
||||||
|
# |_ the `str` can then be modified / passed
|
||||||
|
# around as a variable more easily if needed in
|
||||||
|
# the future ;)
|
||||||
|
#
|
||||||
|
# ALSO, i believe there already is a stdlib
|
||||||
|
# module to do "alignment" of text which you
|
||||||
|
# could try for doing the right-side alignment,
|
||||||
|
# https://docs.python.org/3/library/textwrap.html#textwrap.indent
|
||||||
|
#
|
||||||
print('-----------------------------------------------')
|
print('-----------------------------------------------')
|
||||||
print(f'timestamp: {datetime.fromtimestamp(max_pain['timestamp'])}')
|
print(f'timestamp: {datetime.fromtimestamp(max_pain['timestamp'])}')
|
||||||
print(f'expiry_date: {max_pain['expiry_date']}')
|
print(f'expiry_date: {max_pain['expiry_date']}')
|
||||||
|
|
@ -273,14 +312,27 @@ async def max_pain_daemon(
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
|
||||||
async with tractor.open_nursery() as n:
|
async with tractor.open_nursery(
|
||||||
|
debug_mode=True,
|
||||||
|
loglevel='info',
|
||||||
|
) as an:
|
||||||
|
from tractor import log
|
||||||
|
log.get_console_log(level='info')
|
||||||
|
|
||||||
p: tractor.Portal = await n.start_actor(
|
ptl: tractor.Portal = await an.start_actor(
|
||||||
'max_pain_daemon',
|
'max_pain_daemon',
|
||||||
enable_modules=[__name__],
|
enable_modules=[__name__],
|
||||||
infect_asyncio=True,
|
infect_asyncio=True,
|
||||||
|
# ^TODO, we can actually run this in the root-actor now
|
||||||
|
# if needed as per 2nd "section" in,
|
||||||
|
# https://pikers.dev/goodboy/tractor/pulls/2
|
||||||
|
#
|
||||||
|
# NOTE, will first require us porting to modern
|
||||||
|
# `tractor:main` though ofc!
|
||||||
|
|
||||||
)
|
)
|
||||||
await p.run(max_pain_daemon)
|
await ptl.run(max_pain_daemon)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
trio.run(main)
|
trio.run(main)
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,29 @@
|
||||||
## Max Pain Calculation for Deribit Options
|
## Max Pain Calculation for Deribit Options
|
||||||
|
|
||||||
This feature, which calculates the max pain point for options traded on the Deribit exchange using cryptofeed library.
|
This feature, which calculates the max pain point for options traded
|
||||||
|
on the Deribit exchange using cryptofeed library.
|
||||||
|
|
||||||
- Functions in the api module for fetching options data from Deribit. [commit](https://pikers.dev/pikers/piker/commit/da55856dd2876291f55a06eb0561438a912d8241)
|
- Functions in the api module for fetching options data from Deribit.
|
||||||
|
[commit](https://pikers.dev/pikers/piker/commit/da55856dd2876291f55a06eb0561438a912d8241)
|
||||||
|
|
||||||
- Compute the max pain point based on open interest data using deribit's api. [commit](https://pikers.dev/pikers/piker/commit/0d9d6e15ba0edeb662ec97f7599dd66af3046b94)
|
- Compute the max pain point based on open interest data using
|
||||||
|
deribit's api.
|
||||||
|
[commit](https://pikers.dev/pikers/piker/commit/0d9d6e15ba0edeb662ec97f7599dd66af3046b94)
|
||||||
|
|
||||||
### How to test it?
|
### How to test it?
|
||||||
|
|
||||||
**Before start:** in order to get this working with `uv`, you **must** use my `tractor` [fork](https://pikers.dev/ntorres/tractor/src/branch/aio_abandons) and this branch: `aio_abandons`, the reason is that I cherry-pick the `uv_migration` that guille made, for some reason that a didn't dive into, in my system y need tractor using `uv` too. quite hacky I guess.
|
**Before start:** in order to get this working with `uv`, you
|
||||||
|
**must** use my [`tractor` fork](https://pikers.dev/ntorres/tractor/src/branch/aio_abandons)
|
||||||
|
and this branch: `aio_abandons`, the reason is that I cherry-pick the
|
||||||
|
`uv_migration` that guille made, for some reason that a didn't dive
|
||||||
|
into, in my system y need tractor using `uv` too. quite hacky
|
||||||
|
I guess.
|
||||||
|
|
||||||
1. `uv lock`
|
1. `uv lock`
|
||||||
|
|
||||||
2. `uv run --no-dev python examples/max_pain.py`
|
2. `uv run --no-dev python examples/max_pain.py`
|
||||||
|
|
||||||
3. A message should be display, enter one of the expiration date available.
|
3. A message should be display, enter one of the expiration date
|
||||||
|
available.
|
||||||
|
|
||||||
4. The script should be up and running.
|
4. The script should be up and running.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# piker: trading gear for hackers
|
# piker: trading gear for hackers
|
||||||
# Copyright (C) Guillermo Rodriguez (in stewardship for piker0)
|
# Copyright (C) Guillermo Rodriguez (in stewardship for pikers)
|
||||||
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as published by
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
|
@ -80,7 +80,6 @@ from piker.accounting import (
|
||||||
from piker.data import (
|
from piker.data import (
|
||||||
def_iohlcv_fields,
|
def_iohlcv_fields,
|
||||||
match_from_pairs,
|
match_from_pairs,
|
||||||
# Struct,
|
|
||||||
)
|
)
|
||||||
from piker.data._web_bs import (
|
from piker.data._web_bs import (
|
||||||
open_jsonrpc_session
|
open_jsonrpc_session
|
||||||
|
|
@ -195,7 +194,11 @@ def cb_sym_to_deribit_inst(sym: Symbol) -> str:
|
||||||
|
|
||||||
|
|
||||||
def get_values_from_cb_normalized_date(expiry_date: str) -> str:
|
def get_values_from_cb_normalized_date(expiry_date: str) -> str:
|
||||||
# deribit specific
|
'''
|
||||||
|
Convert the `cryptofeed` (expiry) datetime format to our own,
|
||||||
|
a simple 3 token `str: f'{day}{month}{year}'.
|
||||||
|
|
||||||
|
'''
|
||||||
cb_norm = [
|
cb_norm = [
|
||||||
'F', 'G', 'H', 'J',
|
'F', 'G', 'H', 'J',
|
||||||
'K', 'M', 'N', 'Q',
|
'K', 'M', 'N', 'Q',
|
||||||
|
|
@ -328,7 +331,6 @@ class Client:
|
||||||
'''
|
'''
|
||||||
Return the set of currencies for deribit.
|
Return the set of currencies for deribit.
|
||||||
'''
|
'''
|
||||||
assets = {}
|
|
||||||
resp = await self._json_rpc_auth_wrapper(
|
resp = await self._json_rpc_auth_wrapper(
|
||||||
'public/get_currencies',
|
'public/get_currencies',
|
||||||
params={}
|
params={}
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,10 @@ from ._util import (
|
||||||
get_logger,
|
get_logger,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# ?TODO? this can now be removed since it was originally to extend
|
||||||
|
# with a `bar_vwap` field that we removed from the default ohlcv
|
||||||
|
# dtype since it's better calculated in an FSP func
|
||||||
|
#
|
||||||
_bar_load_dtype: list[tuple[str, type]] = [
|
_bar_load_dtype: list[tuple[str, type]] = [
|
||||||
# NOTE XXX: only part that's diff
|
# NOTE XXX: only part that's diff
|
||||||
# from our default fields where
|
# from our default fields where
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue