Skip to content

Commit ed382da

Browse files
authored
๐Ÿ—ƒ๏ธ update position data structure (#102)
๐Ÿ—ƒ๏ธ update position data structure chore: release version 0.9.5 --- <details open><summary>Generated summary (powered by <a href="https://app.graphite.dev">Graphite</a>)</summary> > ## TL;DR > This pull request updates the version of the `pyrb` package to `0.9.5`, introduces new asset class enums, refactors the `Position` model to include an `Asset` object, and updates related code to reflect these changes. > > ## What changed > - Updated the version of the `pyrb` package to `0.9.5` in `pyproject.toml`. > - Added new asset class enums (`STOCK`, `BOND`, `CASH`, `COMMODITY`, `OTHER`) in `enums.py`. > - Refactored the `Position` model to include an `Asset` object with `symbol` and `label` fields. > - Updated code in various files to use the new `Asset` object instead of directly accessing the `symbol` field. > > ## How to test > 1. Update the `pyrb` package to version `0.9.5`. > 2. Check the new asset class enums (`STOCK`, `BOND`, `CASH`, `COMMODITY`, `OTHER`) in `enums.py`. > 3. Verify that the `Position` model now includes an `Asset` object with `symbol` and `label` fields. > 4. Test related code changes in `controllers/cli/main.py`, `repositories/brokerages/ebest/portfolio.py`, `tests/conftest.py`, and `tests/controllers/test_api.py`. > > ## Why make this change > - Introduces a more structured approach to handling asset classes in the `pyrb` package. > - Enhances the readability and maintainability of the code by using an `Asset` object in the `Position` model. > - Aligns the codebase with best practices for modeling financial assets and positions. </details>
1 parent 9f0b1e1 commit ed382da

File tree

7 files changed

+49
-15
lines changed

7 files changed

+49
-15
lines changed

โ€Žpyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "pyrb"
3-
version = "0.9.4"
3+
version = "0.9.5"
44
description = "Python Rebalancer"
55
authors = ["Minki Kim <[email protected]>"]
66
readme = "README.md"

โ€Žpyrb/controllers/cli/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ def _print_portfolio_table(context: RebalanceContext) -> None:
229229
rtn_style = "blue"
230230

231231
table.add_row(
232-
position.symbol,
232+
position.asset.symbol,
233233
_format(position.quantity, "number"),
234234
_format(position.sellable_quantity, "number"),
235235
_format(position.average_buy_price, "currency"),

โ€Žpyrb/enums.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,11 @@ class OrderSide(StrEnum):
2323

2424
class AssetAllocationStrategyEnum(StrEnum):
2525
ALL_WEATHER_KR = "all-weather-kr"
26+
27+
28+
class AssetClassEnum(StrEnum):
29+
STOCK = "STOCK"
30+
BOND = "BOND"
31+
CASH = "CASH"
32+
COMMODITY = "COMMODITY"
33+
OTHER = "OTHER"

โ€Žpyrb/models/position.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
1-
from pydantic import BaseModel, PositiveFloat, PositiveInt
1+
from pydantic import BaseModel, PositiveFloat, PositiveInt, computed_field
22

3+
from pyrb.enums import AssetClassEnum
34

4-
class Position(BaseModel):
5+
asset_class_by_symbols: dict[str, AssetClassEnum] = {
6+
"361580": AssetClassEnum.STOCK, # KBSTAR 200TR
7+
"379800": AssetClassEnum.STOCK, # KODEX ๋ฏธ๊ตญS&P500TR
8+
"411060": AssetClassEnum.COMMODITY, # ACE KRX๊ธˆํ˜„๋ฌผ
9+
"365780": AssetClassEnum.BOND, # ACE ๊ตญ๊ณ ์ฑ„10๋…„
10+
"308620": AssetClassEnum.BOND, # KODEX ๋ฏธ๊ตญ์ฑ„10๋…„์„ ๋ฌผ
11+
"272580": AssetClassEnum.CASH, # TIGER ๋‹จ๊ธฐ์ฑ„๊ถŒ์•กํ‹ฐ๋ธŒ
12+
"005930": AssetClassEnum.STOCK, # ์‚ผ์„ฑ์ „์ž
13+
"000660": AssetClassEnum.STOCK, # SKํ•˜์ด๋‹‰์Šค
14+
}
15+
16+
17+
class Asset(BaseModel):
518
symbol: str # ์ข…๋ชฉ์ฝ”๋“œ
19+
label: str # ์ข…๋ชฉ๋ช…
20+
21+
@computed_field
22+
def asset_class(self) -> str:
23+
return asset_class_by_symbols.get(self.symbol, AssetClassEnum.OTHER)
24+
25+
26+
class Position(BaseModel):
27+
asset: Asset # ์ข…๋ชฉ์ฝ”๋“œ
628
quantity: PositiveInt # ๋ณด์œ ์ˆ˜๋Ÿ‰
729
sellable_quantity: PositiveInt # ๋งค๋„๊ฐ€๋Šฅ์ˆ˜๋Ÿ‰
830
average_buy_price: PositiveFloat # ๋งค์ž…๋‹จ๊ฐ€

โ€Žpyrb/repositories/brokerages/ebest/portfolio.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from pydantic import NonNegativeFloat
44

5-
from pyrb.models.position import Position
5+
from pyrb.models.position import Asset, Position
66
from pyrb.repositories.brokerages.base.portfolio import Portfolio
77
from pyrb.repositories.brokerages.ebest.client import EbestAPIClient
88

@@ -24,7 +24,7 @@ def cash_balance(self) -> NonNegativeFloat:
2424
def positions(self) -> list[Position]:
2525
positions = [
2626
Position(
27-
symbol=item["expcode"],
27+
asset=Asset(symbol=item["expcode"], label=item["hname"]),
2828
quantity=item["janqty"],
2929
sellable_quantity=item["mdposqt"],
3030
average_buy_price=item["pamt"],
@@ -38,10 +38,12 @@ def positions(self) -> list[Position]:
3838

3939
@property
4040
def holding_symbols(self) -> list[str]:
41-
return [position.symbol for position in self.positions]
41+
return [position.asset.symbol for position in self.positions]
4242

4343
def get_position(self, symbol: str) -> Position | None:
44-
return next((position for position in self.positions if position.symbol == symbol), None)
44+
return next(
45+
(position for position in self.positions if position.asset.symbol == symbol), None
46+
)
4547

4648
def get_position_amount(self, symbol: str) -> NonNegativeFloat:
4749
position = self.get_position(symbol)

โ€Žtests/conftest.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import pytest
66

77
from pyrb.models.order import Order
8-
from pyrb.models.position import Position
8+
from pyrb.models.position import Asset, Position
99
from pyrb.models.price import CurrentPrice
1010
from pyrb.repositories.account import AccountRepository, LocalConfigAccountRepository
1111
from pyrb.repositories.brokerages.base.fetcher import PriceFetcher
@@ -30,15 +30,15 @@ def cash_balance(self) -> float:
3030
def positions(self) -> list[Position]:
3131
return [
3232
Position(
33-
symbol="000660",
33+
asset=Asset(symbol="000660", label="SKํ•˜์ด๋‹‰์Šค"),
3434
quantity=100,
3535
sellable_quantity=100,
3636
average_buy_price=100,
3737
total_amount=10000,
3838
rtn=0.0,
3939
),
4040
Position(
41-
symbol="005930",
41+
asset=Asset(symbol="005930", label="์‚ผ์„ฑ์ „์ž"),
4242
quantity=50,
4343
sellable_quantity=50,
4444
average_buy_price=150,
@@ -49,10 +49,12 @@ def positions(self) -> list[Position]:
4949

5050
@property
5151
def holding_symbols(self) -> list[str]:
52-
return [position.symbol for position in self.positions]
52+
return [position.asset.symbol for position in self.positions]
5353

5454
def get_position(self, symbol: str) -> Position | None:
55-
return next((position for position in self.positions if position.symbol == symbol), None)
55+
return next(
56+
(position for position in self.positions if position.asset.symbol == symbol), None
57+
)
5658

5759
def get_position_amount(self, symbol: str) -> float:
5860
position = self.get_position(symbol)

โ€Žtests/controllers/test_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,15 @@ def test_get_portfolio(fake_rebalance_context: RebalanceContext) -> None:
9999
"cash_balance": 0,
100100
"positions": [
101101
{
102-
"symbol": "000660",
102+
"asset": {"symbol": "000660", "label": "SKํ•˜์ด๋‹‰์Šค", "asset_class": "STOCK"},
103103
"quantity": 100,
104104
"sellable_quantity": 100,
105105
"average_buy_price": 100,
106106
"total_amount": 10000,
107107
"rtn": 0.0,
108108
},
109109
{
110-
"symbol": "005930",
110+
"asset": {"symbol": "005930", "label": "์‚ผ์„ฑ์ „์ž", "asset_class": "STOCK"},
111111
"quantity": 50,
112112
"sellable_quantity": 50,
113113
"average_buy_price": 150,

0 commit comments

Comments
ย (0)