Files
quant-os/platforms/fake_joinquant_harness.py
T

192 lines
5.6 KiB
Python

"""Small deterministic JoinQuant API harness for local contract tests."""
import datetime
from types import SimpleNamespace
class FakePosition(object):
def __init__(self, total_amount=0, closeable_amount=None):
self.total_amount = int(total_amount)
self.closeable_amount = int(
total_amount if closeable_amount is None else closeable_amount
)
class FakeJoinQuantHarness(object):
"""Install fake hosted functions into a strategy module."""
def __init__(
self,
histories,
total_value=1_000_000.0,
positions=None,
st_symbols=None,
):
self.histories = {
symbol: [float(value) for value in values]
for symbol, values in histories.items()
}
self.options = {}
self.benchmark = None
self.order_cost = None
self.slippage = None
self.schedules = []
self.orders = []
self.order_styles = []
self.last_index_request = None
self.last_history_request = None
self.last_extras_request = None
self.st_symbols = set(st_symbols or [])
self.current_data = {
symbol: SimpleNamespace(
paused=False,
is_st=False,
high_limit=9999.0,
low_limit=0.01,
day_open=float(values[-1]),
last_price=float(values[-1]),
)
for symbol, values in self.histories.items()
}
self.context = SimpleNamespace(
current_dt=datetime.datetime(2026, 7, 20, 9, 30),
previous_date=datetime.date(2026, 7, 17),
portfolio=SimpleNamespace(
total_value=float(total_value),
positions=dict(positions or {}),
)
)
self.g = SimpleNamespace()
def install(self, module):
module.g = self.g
module.set_option = self.set_option
module.run_daily = self.run_daily
module.attribute_history = self.attribute_history
module.history = self.history
module.get_index_stocks = self.get_index_stocks
module.get_extras = self.get_extras
module.set_benchmark = self.set_benchmark
module.set_order_cost = self.set_order_cost
module.set_slippage = self.set_slippage
module.OrderCost = self.OrderCost
module.PriceRelatedSlippage = self.PriceRelatedSlippage
module.LimitOrderStyle = self.LimitOrderStyle
module.order_target = self.order_target
module.get_current_data = self.get_current_data
return self
def set_option(self, key, value):
self.options[key] = value
def set_benchmark(self, symbol):
self.benchmark = symbol
@staticmethod
def OrderCost(**kwargs):
return SimpleNamespace(**kwargs)
@staticmethod
def PriceRelatedSlippage(value):
return SimpleNamespace(value=float(value))
@staticmethod
def LimitOrderStyle(limit_price):
return SimpleNamespace(
kind="limit",
limit_price=float(limit_price),
)
def set_order_cost(self, cost, type=None):
self.order_cost = {"cost": cost, "type": type}
def set_slippage(self, slippage, type=None):
self.slippage = {"slippage": slippage, "type": type}
def run_daily(self, callback, time="open"):
self.schedules.append((callback, time))
def attribute_history(
self,
symbol,
count,
unit="1d",
fields=None,
skip_paused=False,
df=False,
fq="pre",
):
del unit, fields, skip_paused, df, fq
return {"close": self.histories[symbol][-int(count) :]}
def history(
self,
count,
unit="1d",
field="close",
security_list=None,
df=False,
skip_paused=False,
fq="pre",
):
del unit, field, df, skip_paused, fq
symbols = list(security_list or self.histories)
self.last_history_request = {
"count": int(count),
"security_list": symbols,
}
return {
symbol: self.histories[symbol][-int(count) :]
for symbol in symbols
}
def get_index_stocks(self, index_symbol, date=None):
self.last_index_request = {
"index_symbol": index_symbol,
"date": date,
}
return sorted(self.histories)
def get_extras(
self,
info,
security_list,
end_date=None,
count=1,
df=False,
):
self.last_extras_request = {
"info": info,
"security_list": list(security_list),
"end_date": end_date,
"count": count,
"df": df,
}
return {
symbol: [symbol in self.st_symbols]
for symbol in security_list
}
def order_target(self, symbol, quantity, style=None):
self.orders.append((symbol, int(quantity)))
self.order_styles.append((symbol, style))
return SimpleNamespace(security=symbol, target_quantity=int(quantity))
def get_current_data(self):
return self.current_data
def initialize(self, module):
self.install(module)
module.initialize(self.context)
return self
def run_scheduled(self, index=0):
callback = self.schedules[index][0]
return callback(self.context)
def advance_session(self, days=1):
previous = self.context.current_dt.date()
self.context.previous_date = previous
self.context.current_dt += datetime.timedelta(days=int(days))
return self