Skip to content

Commit

Permalink
Merge pull request #21 from 8ball030/feat/analyser-class
Browse files Browse the repository at this point in the history
[feat] moved analyser to class
  • Loading branch information
8ball030 authored Feb 1, 2024
2 parents f52a5b9 + 9374240 commit 3e6e40b
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 45 deletions.
50 changes: 50 additions & 0 deletions lyra/analyser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""
Class based analyser for portfolios.
"""

from typing import List, Optional

import pandas as pd

pd.set_option('display.precision', 2)


DELTA_COLUMNS = ['delta', 'gamma', 'vega', 'theta']


class PortfolioAnalyser:
raw_data: List[dict]
df: pd.DataFrame

def __init__(self, raw_data: List[dict]):
self.raw_data = raw_data
self.positions = pd.DataFrame.from_records(raw_data['positions'])
self.positions["amount"] = pd.to_numeric(self.positions["amount"])
for col in DELTA_COLUMNS:
self.positions[col] = pd.to_numeric(self.positions[col])
adjusted_greek = self.positions[col] * self.positions.amount
self.positions[col] = adjusted_greek

self.positions = self.positions.apply(pd.to_numeric, errors='ignore')

def get_positions(self, underlying_currency: str) -> pd.DataFrame:
df = self.positions
df = df[df['instrument_name'].str.contains(underlying_currency.upper())]
return df

def get_open_positions(self, underlying_currency: str) -> pd.DataFrame:
df = self.get_positions(underlying_currency)
return df[df['amount'] != 0]

def get_total_greeks(self, underlying_currency: str) -> pd.DataFrame:
df = self.get_open_positions(underlying_currency)
return df[DELTA_COLUMNS].sum()

def get_subaccount_value(self) -> float:
return float(self.raw_data['subaccount_value'])

def print_positions(self, underlying_currency: str, columns: Optional[List[str]] = None):
df = self.get_open_positions(underlying_currency)
if columns:
df = df[[c for c in columns if c not in DELTA_COLUMNS] + DELTA_COLUMNS]
print(df)
51 changes: 9 additions & 42 deletions lyra/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from dotenv import load_dotenv
from rich import print

from lyra.analyser import PortfolioAnalyser
from lyra.enums import (
CollateralAsset,
Environment,
Expand All @@ -22,6 +23,7 @@
from lyra.utils import get_logger

click.rich_click.USE_RICH_MARKUP = True
pd.set_option('display.precision', 2)


def set_logger(ctx, level):
Expand Down Expand Up @@ -223,52 +225,17 @@ def fetch_subaccounts(ctx):
def fetch_subaccount(ctx, subaccount_id, underlying_currency, columns):
"""Fetch subaccount."""
print("Fetching subaccount")
print(f"Subaccount ID: {subaccount_id}")
print(f"Underlying currency: {underlying_currency}")
client = ctx.obj["client"]
subaccount = client.fetch_subaccount(subaccount_id=subaccount_id)
print(subaccount)
pd.set_option('display.precision', 2)

df = pd.DataFrame.from_records(subaccount["collaterals"])

positions = subaccount["positions"]

df = pd.DataFrame.from_records(positions)
df["amount"] = pd.to_numeric(df["amount"])
delta_columns = ['delta', 'gamma', 'vega', 'theta']
for col in delta_columns:
df[col] = pd.to_numeric(df[col])
if columns:
columns = columns.split(",")
df = df[[c for c in columns if c not in delta_columns] + delta_columns]
analyser = PortfolioAnalyser(subaccount)
print("Positions")
open_positions = df[df['amount'] != 0]
open_positions = open_positions[open_positions['instrument_name'].str.contains(underlying_currency.upper())]
print("Greeks")
for col in delta_columns:
position_adjustment = open_positions[col] * df.amount
open_positions[col] = position_adjustment
print("Open positions")

pd.options.display.float_format = "{:,.2f}".format

open_positions = open_positions.sort_values(by=['instrument_name'])
if columns:

for col in columns:
try:
col = pd.to_numeric(open_positions[col])
except:
pass
print(open_positions[columns])
else:
print(open_positions)

# total deltas
print("Total deltas")
print(open_positions[delta_columns].sum())

analyser.print_positions(underlying_currency=underlying_currency, columns=columns)
print("Total Greeks")
print(analyser.get_total_greeks(underlying_currency))
print("Subaccount values")
print(subaccount['subaccount_value'])
print(f"Portfolio Value: ${analyser.get_subaccount_value():.2f}")


@subaccounts.command("create")
Expand Down
25 changes: 22 additions & 3 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import pytest
import requests

from lyra.analyser import PortfolioAnalyser
from lyra.enums import (
ActionType,
CollateralAsset,
Expand Down Expand Up @@ -39,7 +40,8 @@ def freeze_time(lyra_client):
def lyra_client():
lyra_client = LyraClient(TEST_PRIVATE_KEY, env=Environment.TEST, logger=get_logger())
lyra_client.subaccount_id = 5
return lyra_client
yield lyra_client
lyra_client.cancel_all()


def test_lyra_client(lyra_client):
Expand Down Expand Up @@ -220,8 +222,8 @@ def test_get_tickers(lyra_client):
@pytest.mark.parametrize(
"currency, side",
[
(UnderlyingCurrency.ETH, OrderSide.BUY),
(UnderlyingCurrency.ETH, OrderSide.SELL),
# (UnderlyingCurrency.ETH, OrderSide.BUY),
# (UnderlyingCurrency.ETH, OrderSide.SELL),
(UnderlyingCurrency.BTC, OrderSide.BUY),
(UnderlyingCurrency.BTC, OrderSide.SELL),
],
Expand Down Expand Up @@ -435,3 +437,20 @@ def test_transfer_collateral_steps(
break
else:
assert False, "No subaccount has a balance"


@pytest.mark.parametrize(
"underlying_currency",
[
UnderlyingCurrency.BTC.value,
],
)
def test_analyser(underlying_currency, lyra_client):
"""Test analyser."""
raw_data = lyra_client.fetch_subaccount(lyra_client.subaccount_id)
analyser = PortfolioAnalyser(raw_data)
analyser.print_positions(underlying_currency)
assert len(analyser.get_positions(underlying_currency))
assert len(analyser.get_open_positions(underlying_currency))
assert analyser.get_subaccount_value()
assert len(analyser.get_total_greeks(underlying_currency))

0 comments on commit 3e6e40b

Please sign in to comment.