192 lines
5.6 KiB
Python
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
|