Skip to content

Commit

Permalink
Merge pull request #69 from masanorihirano/release/0.0.14
Browse files Browse the repository at this point in the history
Release/0.0.14
  • Loading branch information
masanorihirano authored Apr 17, 2023
2 parents 2bc0b20 + ced3670 commit 51162b7
Show file tree
Hide file tree
Showing 11 changed files with 587 additions and 12 deletions.
4 changes: 2 additions & 2 deletions docs/source/user_guide/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ Json config
"target": "SpotMarket-1",
"triggerTime": 0,
"priceChangeRate": -0.1,
"shockTimeLength": 2,
"enabled": true
"shockTimeLength": int (Optional, default: 1),
"enabled": bool (Optional, default: True)
},
"Market": {
"class": "string required",
Expand Down
16 changes: 14 additions & 2 deletions pams/agents/arbitrage_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def setup( # type: ignore
settings: Dict[str, Any],
accessible_markets_ids: List[int],
*args,
**kwargs
**kwargs,
) -> None:
"""agent setup. Usually be called from simulator/runner automatically.
Expand Down Expand Up @@ -96,7 +96,7 @@ def _submit_orders(self, market: Market) -> List[Union[Order, Cancel]]:
market_price: float = index.get_market_price()

if len(set(map(lambda x: x.outstanding_shares, spots))) > 1:
raise AssertionError(
raise NotImplementedError(
"currently, the components must have the same outstanding shares"
)

Expand Down Expand Up @@ -168,3 +168,15 @@ def submit_orders(self, markets: List[Market]) -> List[Union[Order, Cancel]]:
for market in markets:
orders.extend(self._submit_orders(market=market))
return orders

def __repr__(self) -> str:
"""string representation of FCN agent class.
Returns:
str: string representation of this class.
"""
return (
f"<{self.__class__.__module__}.{self.__class__.__name__} | id={self.agent_id}, rnd={self.prng}, "
f"order_volume={self.order_volume}, order_threshold_price={self.order_threshold_price}, "
f"order_time_length={self.order_time_length}>"
)
26 changes: 21 additions & 5 deletions pams/events/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
from typing import List
from typing import Optional
from typing import Type
from typing import cast

from pams.market import Market


class EventHook:
Expand Down Expand Up @@ -59,16 +62,29 @@ def __init__(
raise ValueError(
"specific_class and specific_instance are not supported except for market"
)
self.specific_class: Optional[Type] = (
specific_class.__class__ if specific_class is not None else None
)
else:
if hook_type == "market":
if specific_class is not None and not issubclass(
specific_class, Market
):
raise ValueError("specific_class and hook_type is incompatible")
if specific_instance is not None and not isinstance(
specific_instance, Market
):
raise ValueError(
"specific_instance and hook_type is incompatible"
)
else:
raise AssertionError
specific_class = cast(Optional[Type], specific_class)
self.specific_class: Optional[Type] = specific_class
self.specific_instance: Optional[object] = specific_instance

def __repr__(self) -> str:
return (
f"<{self.__class__.__module__}.{self.__class__.__name__} | hook_type={self.hook_type}, "
f"is_before={self.is_before}, time={self.time}, event={self.event}, specific_class={self.specific_class}, "
f"specific_instance={self.specific_instance}>"
f"is_before={self.is_before}, time={self.time}, specific_class={self.specific_class}, "
f"specific_instance={self.specific_instance}, event={self.event}>"
)


Expand Down
4 changes: 4 additions & 0 deletions pams/events/fundamental_price_shock.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,17 @@ def setup(self, settings: Dict[str, Any], *args, **kwargs) -> None: # type: ign
self.target_market_name = settings["target"]
if "triggerTime" not in settings:
raise ValueError("triggerTime is required for FundamentalPriceShock")
if not isinstance(settings["triggerTime"], int):
raise ValueError("triggerTime have to be int")
self.trigger_time = self.session.session_start_time + settings["triggerTime"]
if "priceChangeRate" not in settings:
raise ValueError("priceChangeRate is required for FundamentalPriceShock")
self.price_change_rate = settings["priceChangeRate"]
if "enabled" in settings:
self.is_enabled = settings["enabled"]
if "shockTimeLength" in settings:
if not isinstance(settings["shockTimeLength"], int):
raise ValueError("shockTimeLength have to be int")
self.shock_time_length = settings["shockTimeLength"]
self.target_market = self.simulator.name2market[self.target_market_name]

Expand Down
2 changes: 1 addition & 1 deletion pams/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def __repr__(self) -> str:
f"iteration_steps={self.iteration_steps}, session_start_time={self.session_start_time}, "
f"max_normal_orders={self.max_normal_orders}, max_high_frequency_orders={self.max_high_frequency_orders}, "
f"with_order_placement={self.with_order_placement}, with_order_execution={self.with_order_execution}, "
f"high_frequency_submission_rate={self.high_frequency_submission_rate}, with_print={self.with_print},"
f"high_frequency_submission_rate={self.high_frequency_submission_rate}, with_print={self.with_print}, "
f"logger={self.logger.__str__()}>"
)

Expand Down
2 changes: 1 addition & 1 deletion pams/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.0.13"
__version__ = "0.0.14"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pams"
version = "0.0.13"
version = "0.0.14"
description = "PAMS: Platform for Artificial Market Simulations"
authors = ["Masanori HIRANO <[email protected]>"]
license = "MIT"
Expand Down
60 changes: 60 additions & 0 deletions tests/pams/agents/test_arbitrage_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,32 @@ def test_setup(self) -> None:
}
agent.setup(settings=settings5, accessible_markets_ids=[0, 1, 2])
assert agent.order_time_length == 1
agent = ArbitrageAgent(
agent_id=1,
prng=random.Random(42),
simulator=sim,
name="test_agent",
logger=logger,
)
settings6 = {"assetVolume": 50, "cashAmount": 10000, "orderVolume": 1}
with pytest.raises(ValueError):
agent.setup(settings=settings6, accessible_markets_ids=[0, 1, 2])
agent = ArbitrageAgent(
agent_id=1,
prng=random.Random(42),
simulator=sim,
name="test_agent",
logger=logger,
)
settings5 = {
"assetVolume": 50,
"cashAmount": 10000,
"orderVolume": 1,
"orderThresholdPrice": 0.1,
"orderTimeLength": 10.1,
}
with pytest.raises(ValueError):
agent.setup(settings=settings5, accessible_markets_ids=[0, 1, 2])

@pytest.mark.parametrize("index_price", [350.0, 350.05, 349.95, 351, 349])
@pytest.mark.parametrize("order_volume", [1, 2])
Expand Down Expand Up @@ -205,3 +231,37 @@ def test_submit_orders(self, index_price: float, order_volume: int) -> None:
assert not order1.is_buy
assert not order2.is_buy
assert index_order.is_buy
index_market._is_running = False
orders = agent.submit_orders(markets=[market1, market2, index_market])
assert len(orders) == 0
index_market._is_running = True
agent2 = ArbitrageAgent(
agent_id=1, prng=_prng, simulator=sim, name="test_agent", logger=logger
)
agent2.setup(settings=settings1, accessible_markets_ids=[0, 1])
orders = agent2.submit_orders(markets=[market1, market2, index_market])
assert len(orders) == 0
market1.outstanding_shares = 1000
with pytest.raises(NotImplementedError):
agent.submit_orders(markets=[market1, market2, index_market])

def test__repr__(self) -> None:
sim = Simulator(prng=random.Random(4))
logger = Logger()
_prng = random.Random(42)
agent = ArbitrageAgent(
agent_id=1, prng=_prng, simulator=sim, name="test_agent", logger=logger
)
settings1 = {
"assetVolume": 50,
"cashAmount": 10000,
"orderVolume": 2,
"orderThresholdPrice": 0.1,
"orderTimeLength": 10,
}
agent.setup(settings=settings1, accessible_markets_ids=[0, 1, 2])
assert (
str(agent)
== f"<pams.agents.arbitrage_agent.ArbitrageAgent | id=1, rnd={_prng}, order_volume=2, "
f"order_threshold_price=0.1, order_time_length=10>"
)
Empty file added tests/pams/events/__init__.py
Empty file.
176 changes: 176 additions & 0 deletions tests/pams/events/test_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import random
from typing import List
from typing import Optional
from typing import Type

import pytest

from pams import Market
from pams import Session
from pams import Simulator
from pams.events import EventABC
from pams.events import EventHook
from pams.events import FundamentalPriceShock
from pams.logs import Logger
from tests.pams.agents.test_base import DummyAgent


class TestEventHook:
@pytest.mark.parametrize(
"hook_type", ["order", "cancel", "execution", "session", "market", "dummy"]
)
@pytest.mark.parametrize("is_before", [True, False])
@pytest.mark.parametrize("specific_class", [None, Market, DummyAgent])
@pytest.mark.parametrize("specific_instance_name", [None, "market", "agent"])
def test(
self,
hook_type: str,
is_before: bool,
specific_class: Optional[Type],
specific_instance_name: Optional[str],
) -> None:
sim = Simulator(prng=random.Random(4))
logger = Logger()
session = Session(
session_id=0,
prng=random.Random(42),
session_start_time=0,
simulator=sim,
name="session0",
logger=logger,
)
session_setting = {
"sessionName": 0,
"iterationSteps": 500,
"withOrderPlacement": True,
"withOrderExecution": True,
"withPrint": True,
"maxNormalOrders": 1,
"events": ["FundamentalPriceShock"],
}
session.setup(settings=session_setting)
sim._add_session(session=session)
market = Market(
market_id=0,
prng=random.Random(42),
simulator=sim,
name="market1",
logger=logger,
)
settings_market = {
"tickSize": 0.01,
"marketPrice": 300.0,
"outstandingShares": 2000,
}
market.setup(settings=settings_market)
agent = DummyAgent(
agent_id=1,
prng=random.Random(42),
simulator=sim,
name="test_agent",
logger=logger,
)
settings_agent = {"assetVolume": 50, "cashAmount": 10000}
agent.setup(settings=settings_agent, accessible_markets_ids=[0])
sim._add_agent(agent=agent)

event = FundamentalPriceShock(
event_id=0,
prng=random.Random(42),
session=session,
simulator=sim,
name="event0",
)
specific_instance: Optional[object]
if specific_instance_name is None:
specific_instance = None
elif specific_instance_name == "market":
specific_instance = market
elif specific_instance_name == "agent":
specific_instance = agent
else:
raise NotImplementedError
if (
(hook_type == "execution" and is_before)
or hook_type == "dummy"
or (hook_type != "market" and specific_class is not None)
or (hook_type != "market" and specific_instance is not None)
or specific_class == DummyAgent
or specific_instance_name == "agent"
):
with pytest.raises(ValueError):
EventHook(
event=event,
hook_type=hook_type,
is_before=is_before,
time=[1, 3],
specific_class=specific_class,
specific_instance=specific_instance,
)
return
else:
event_hook = EventHook(
event=event,
hook_type=hook_type,
is_before=is_before,
time=[1, 3],
specific_class=specific_class,
specific_instance=specific_instance,
)
sim._add_event(event_hook=event_hook)
assert event_hook.event == event
assert event_hook.hook_type == hook_type
assert event_hook.is_before == is_before
assert event_hook.time == [1, 3]
assert event_hook.specific_class == specific_class
assert event_hook.specific_instance == specific_instance
assert (
str(event_hook)
== f"<pams.events.base.EventHook | hook_type={hook_type}, is_before={is_before}, time=[1, 3], "
f"specific_class={specific_class}, specific_instance={specific_instance}, event={event}>"
)


class DummyEvent(EventABC):
def hook_registration(self) -> List[EventHook]:
return []


class TestEventABC:
def test__init__(self) -> EventABC:
sim = Simulator(prng=random.Random(4))
logger = Logger()
session = Session(
session_id=0,
prng=random.Random(42),
session_start_time=0,
simulator=sim,
name="session0",
logger=logger,
)
session_setting = {
"sessionName": 0,
"iterationSteps": 500,
"withOrderPlacement": True,
"withOrderExecution": True,
"withPrint": True,
"maxNormalOrders": 1,
"events": ["FundamentalPriceShock"],
}
session.setup(settings=session_setting)
_prng = random.Random(42)
event = DummyEvent(
event_id=1, prng=_prng, session=session, simulator=sim, name="event"
)
assert event.event_id == 1
assert event.prng == _prng
assert event.simulator == sim
assert event.name == "event"
assert event.session == session
assert (
str(event)
== f"<tests.pams.events.test_base.DummyEvent | id=1, name=event, session={session}>"
)
event.setup(settings={})
assert event.hook_registration() == []
return event
Loading

0 comments on commit 51162b7

Please sign in to comment.