def __init__(self): self.position_manager = LocalPositionManager(app=None) # 每日资金情况 self.daily_life = defaultdict(list) # 回测模式 self.pattern = "t+0" # 起始资金 默认10w 以及冻结 self.balance = 100000 self.frozen = 0 self.size = 5 self.pricetick = 10 self.daily_limit = 20 # 手续费 self.commission: float = 0 # 滑点相关设置 self.slip_page: float = 0 # 当前日志信息 self.date = None
def __init__(self, interface, name=None): """ 核心账户 Args """ self.account_id = name if name is not None else uuid.uuid4() # 成交接口 self.interface = interface # 每日成交单信息 self.daily_life = defaultdict(AliasDayResult) # 合约乘数 self.size_map = {} # 每日下单限制 self.daily_limit = 20 self.pre_balance = 0 # 账户当前的日期 self.date = None self.count_statistics = 0 # 初始资金 self.initial_capital = 0 # 账户权益 # 保证金占用 self.long_margin = 0 self.short_margin = 0 # 冻结的保证金 self.long_frozen_margin = {} self.short_frozen_margin = {} # 冻结的手续费 self.frozen_fee = {} # 多头净值 self.long_balance = 0 # 空头净值 self.short_balance = 0 self.frozen_premium = 0 self.count = 0 """ fee应该是一个 { ag2012.SHFE: 200.1 } """ self.fee = {} self.init_position_manager_flag = False self.init = False self.position_manager = None self.margin_ratio = {} # commission_ratio 应该为{"ag2012.SHFE": {"close_today": 0.005, "close":0.005 } self.basic_info = None self.commission_ratio = defaultdict(dict) self.close_profit = {} self.turnover = 0 self.pre_float = 0 self.position_manager = LocalPositionManager(self) self.code_pnl = {}
def __init__(self, app, event_engine): """""" self.bar = {} self.ticks = {} self.orders = {} self.trades = {} self.positions = {} self.accounts = {} self.contracts = {} self.logs = {} self.errors = {} self.shared = {} self.active_orders = {} self.event_engine = event_engine self.register_event() self.app = app self.position_manager = LocalPositionManager(app=self.app)
def __init__(self, app): """""" self.bar = {} self.ticks = {} self.orders = {} self.trades = {} self.positions = {} self.account = None self.contracts = {} self.logs = {} self.errors = [] self.generators = {} self.active_orders = {} self.local_contract_price_mapping = {} self.app = app self.register_event() self.position_manager = LocalPositionManager(app=self.app) self.main_contract_mapping = defaultdict(list)
def __init__(self, interface): self.interface = interface self.position_manager = LocalPositionManager(interface.params) # 每日资金情况 self.daily_life = defaultdict(list) # 起始资金 默认10w 以及冻结 self.balance = 100000 self.frozen = 0 self.size = 5 self.pricetick = 10 self.daily_limit = 20 # 手续费 self.commission: float = 0 # 滑点相关设置 self.slip_page: float = 0 # 当前日志信息 self.date = None
def update_params(self, params: dict): """ 更新本地账户回测参数 """ for i, v in params.items(): if i == "initial_capital" and not self.init: self.pre_balance = v self.initial_capital = v self.long_balance = v self.short_balance = v self.init = True continue else: pass setattr(self, i, v) if not self.init_position_manager_flag: self.position_manager = LocalPositionManager(self) self.init_position_manager_flag = True else: pass
def __init__(self): self.positions = LocalPositionManager(app=None) # 每日资金情况 self.daily_life = defaultdict(list) { "12-8": AliasDayResult(), "12-9": AliasDayResult(), } # 回测模式 self.pattern = "t+0" # 起始资金 默认10w 以及冻结 self.balance = 100000 self.frozen = 0 self.size = 5 self.pricetick = 10 self.daily_limit = 20 # 手续费 self.commission: float = 0 # 滑点相关设置 self.slip_page: float = 0 self.slip_fixed: float = 0 self.slip_open: bool = False self.slip_match: bool = True self.slip_limit: bool = True self.slip_out: bool = False self.cu = None # 账户持有持仓信息 -----> self.ServberPosition = defaultdict(defaultdict(PositionData))
class AsyncRecorder(object): """ data center """ def __init__(self, app, event_engine): """""" self.main_contract_mapping = defaultdict(list) self.bar = {} self.ticks = {} self.orders = {} self.trades = {} self.positions = {} self.account = None self.contracts = {} self.logs = {} self.errors = [] self.shared = {} self.generators = {} self.active_orders = {} self.event_engine = event_engine self.register_event() self.app = app self.position_manager = LocalPositionManager(app=self.app) @staticmethod def get_local_time(): from datetime import datetime return datetime.now().strftime('%Y-%m-%d %H:%M:%S') def register_event(self): """bind process function""" self.event_engine.register(EVENT_TICK, self.process_tick_event) self.event_engine.register(EVENT_ORDER, self.process_order_event) self.event_engine.register(EVENT_TRADE, self.process_trade_event) self.event_engine.register(EVENT_POSITION, self.process_position_event) self.event_engine.register(EVENT_ACCOUNT, self.process_account_event) self.event_engine.register(EVENT_CONTRACT, self.process_contract_event) self.event_engine.register(EVENT_BAR, self.process_bar_event) self.event_engine.register(EVENT_LOG, self.process_log_event) self.event_engine.register(EVENT_ERROR, self.process_error_event) self.event_engine.register(EVENT_SHARED, self.process_shared_event) self.event_engine.register(EVENT_LAST, self.process_last_event) self.event_engine.register(EVENT_INIT_FINISHED, self.process_init_event) self.event_engine.register(EVENT_TIMER, self.process_timer_event) async def process_timer_event(self): for x in self.app.extensions.values(): await x() async def process_init_event(self, event): """ 处理初始化完成事件 """ if event.data: self.app.init_finished = True for x in self.app.extensions.values(): await x(deepcopy(event)) async def process_last_event(self, event): """ 处理合约的最新行情数据 """ data = event.data # 过滤掉数字 取中文做key key = "".join([x for x in data.symbol if not x.isdigit()]) self.main_contract_mapping[key.upper()].append(data) @property def main_contract_list(self): """ 返回主力合约列表 """ result = [] for _ in self.main_contract_mapping.values(): x = sorted(_, key=lambda x: x.open_interest, reverse=True)[0] result.append(x.local_symbol) return result def get_main_contract_by_code(self, code: str): """ 根据code取相应的主力合约 """ d = self.main_contract_mapping.get(code.upper(), None) if not d: return None else: now = sorted(d, key=lambda x: x.open_interest, reverse=True)[0] pre = sorted(d, key=lambda x: x.pre_open_interest, reverse=True)[0] if pre.local_symbol == now.local_symbol: return pre else: return now @async_value_call async def process_shared_event(self, event): if self.shared.get(event.data.local_symbol, None) is not None: self.shared[event.data.local_symbol].append(event.data) else: self.shared[event.data.local_symbol] = [] async def process_error_event(self, event: Event): self.errors.append({"time": self.get_local_time(), "data": event.data}) self.app.logger.error(event.data) async def process_log_event(self, event: Event): self.logs[self.get_local_time()] = event.data if self.app.config.get("LOG_OUTPUT"): self.app.logger.info(event.data) @async_value_call async def process_bar_event(self, event: Event): bar = event.data local = self.bar.get(bar.local_symbol) if local is None: self.bar[bar.local_symbol] = {bar.interval: []} else: if self.bar[bar.local_symbol].get(bar.interval) is None: self.bar[bar.local_symbol] = {bar.interval: []} self.bar[bar.local_symbol][bar.interval].append(bar) @async_value_call async def process_tick_event(self, event: Event): """""" tick = event.data self.ticks[tick.local_symbol] = tick symbol = tick.symbol self.position_manager.update_tick(tick) # 生成datetime对象 if not tick.datetime: if '.' in tick.time: tick.datetime = datetime.strptime( ' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f') else: tick.datetime = datetime.strptime( ' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S') bm = self.generators.get(symbol, None) if bm: bm.update_tick(tick) if not bm: self.generators[symbol] = generator(self.event_engine, self.app) @async_value_call async def process_order_event(self, event: Event): """""" order = event.data self.orders[order.local_order_id] = order # If order is active, then update data in dict. if order._is_active(): self.active_orders[order.local_order_id] = order # Otherwise, pop inactive order from in dict elif order.local_order_id in self.active_orders: self.active_orders.pop(order.local_order_id) self.position_manager.update_order(order) @async_value_call async def process_trade_event(self, event: Event): """""" trade = event.data self.trades[trade.local_trade_id] = trade self.position_manager.update_trade(trade) @async_value_call async def process_position_event(self, event: Event): """""" position = event.data self.positions[position.local_position_id] = position self.position_manager.update_position(position) async def process_account_event(self, event: Event): """""" account = event.data self.account = account for value in self.app.extensions.values(): await value(deepcopy(event)) async def process_contract_event(self, event: Event): """""" contract = event.data self.contracts[contract.local_symbol] = contract for value in self.app.extensions.values(): await value(deepcopy(event)) def get_shared(self, symbol): return self.shared.get(symbol, None) def get_all_shared(self): return self.shared def get_bar(self, local_symbol): return self.bar.get(local_symbol, None) def get_all_bar(self): return self.bar def get_tick(self, local_symbol): return self.ticks.get(local_symbol, None) def get_order(self, local_order_id): return self.orders.get(local_order_id, None) def get_trade(self, local_trade_id): return self.trades.get(local_trade_id, None) def get_position(self, local_position_id): return self.positions.get(local_position_id, None) def get_account(self): return self.account def get_contract(self, local_symbol): return self.contracts.get(local_symbol, None) def get_all_ticks(self): """ Get all tick data. """ return list(self.ticks.values()) def get_all_orders(self): """ Get all order data. """ return list(self.orders.values()) def get_all_trades(self): """ Get all trade data. """ return list(self.trades.values()) def get_all_positions(self): """ Get all position data. """ # return list(self.positions.values()) return self.position_manager.get_all_positions() def get_errors(self): return self.errors def get_new_error(self): return self.errors[-1] def get_all_contracts(self): """ Get all contract data. """ return list(self.contracts.values()) def get_all_active_orders(self, local_symbol: str = ""): if not local_symbol: return list(self.active_orders.values()) else: active_orders = [ order for order in self.active_orders.values() if order.local_symbol == local_symbol ] return active_orders
class Recorder(object): """ data center """ def __init__(self, app): """""" self.bar = {} self.ticks = {} self.orders = {} self.trades = {} self.positions = {} self.account = None self.contracts = {} self.logs = {} self.errors = [] self.generators = {} self.active_orders = {} self.local_contract_price_mapping = {} self.app = app self.register_event() self.position_manager = LocalPositionManager(app=self.app) self.main_contract_mapping = defaultdict(list) @staticmethod def get_local_time(): from datetime import datetime return datetime.now().strftime('%Y-%m-%d %H:%M:%S') def register_event(self): """ bind process function """ function = lambda name: lambda event: getattr( self, f"process_{name}_event")(event) def connect(data): name = data[0] signal = data[1] temp_sig = getattr(signal, f"{name}_signal") temp_sig.connect(function(name=name), weak=False) return name def generate_params(data, signal): temp = [] for x in data: temp.append((x, signal)) return temp p = list( map( connect, generate_params(signal.common_signals.event, signal.common_signals))) p = list( map( connect, generate_params(self.app.app_signal.event, self.app.app_signal))) def process_timer_event(self, event): for x in self.app.extensions.values(): x() def process_init_event(self, event): """ 处理初始化完成事件 """ if event.data: self.app.init_finished = True for x in self.app.extensions.values(): x(deepcopy(event)) def process_last_event(self, event): """ 处理合约的最新行情数据 """ data = event.data self.local_contract_price_mapping[data.local_symbol] = data.last_price # 过滤掉数字 取中文做key key = "".join([x for x in data.symbol if not x.isdigit()]) self.main_contract_mapping[key.upper()].append(data) def process_error_event(self, event: Event): self.errors.append({"time": self.get_local_time(), "data": event.data}) self.app.logger.error(event.data) def process_log_event(self, event: Event): self.logs[self.get_local_time()] = event.data if self.app.config.get("LOG_OUTPUT"): self.app.logger.info(event.data) @value_call def process_bar_event(self, event: Event): bar = event.data local = self.bar.get(bar.local_symbol) if local is None: self.bar[bar.local_symbol] = {bar.interval: []} else: if self.bar[bar.local_symbol].get(bar.interval) is None: self.bar[bar.local_symbol] = {bar.interval: []} self.bar[bar.local_symbol][bar.interval].append(bar) @value_call def process_tick_event(self, event: Event): """""" tick = event.data self.ticks[tick.local_symbol] = tick self.position_manager.update_tick(tick) # 生成datetime对象 if not tick.datetime: if '.' in tick.time: tick.datetime = datetime.strptime( ' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f') else: tick.datetime = datetime.strptime( ' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S') bm = self.generators.get(tick.local_symbol, None) if bm: bm.update_tick(tick) if not bm: self.generators[tick.local_symbol] = generator(self.app) self.generators[tick.local_symbol].update_tick(tick) @value_call def process_order_event(self, event: Event): """""" order = event.data self.orders[order.local_order_id] = order # If order is active, then update data in dict. if order._is_active(): self.active_orders[order.local_order_id] = order # Otherwise, pop inactive order from in dict elif order.local_order_id in self.active_orders: self.active_orders.pop(order.local_order_id) self.position_manager.update_order(order) @value_call def process_trade_event(self, event: Event): """""" trade = event.data self.trades[trade.local_trade_id] = trade self.position_manager.update_trade(trade) for value in self.app.extensions.values(): if self.app.config['INSTRUMENT_INDEPEND']: if len(value.instrument_set) == 0: warnings.warn( "你当前开启策略对应订阅行情功能, 当前策略的订阅行情数量为0,请确保你的订阅变量是否为instrument_set,以及订阅具体代码" ) if event.data.local_symbol in value.instrument_set: value(deepcopy(event)) else: value(deepcopy(event)) @value_call def process_position_event(self, event: Event): """""" position = event.data self.positions[position.local_position_id] = position self.position_manager.update_position(position) for value in self.app.extensions.values(): if self.app.config['INSTRUMENT_INDEPEND']: if len(value.instrument_set) == 0: warnings.warn( "你当前开启策略对应订阅行情功能, 当前策略的订阅行情数量为0,请确保你的订阅变量是否为instrument_set,以及订阅具体代码" ) if event.data.local_symbol in value.instrument_set: value(deepcopy(event)) else: value(deepcopy(event)) def process_account_event(self, event: Event): """""" account = event.data self.account = account for value in self.app.extensions.values(): value(deepcopy(event)) def process_contract_event(self, event: Event): """""" contract = event.data self.contracts[contract.local_symbol] = contract for value in self.app.extensions.values(): value(deepcopy(event)) def get_shared(self, symbol): return self.shared.get(symbol, None) def get_all_shared(self): return self.shared def get_bar(self, local_symbol): return self.bar.get(local_symbol, None) def get_all_bar(self): return self.bar def get_tick(self, local_symbol): return self.ticks.get(local_symbol, None) def get_order(self, local_order_id): return self.orders.get(local_order_id, None) def get_trade(self, local_trade_id): return self.trades.get(local_trade_id, None) def get_position(self, local_position_id): return self.positions.get(local_position_id, None) def get_account(self): return self.account def get_contract(self, local_symbol): return self.contracts.get(local_symbol, None) def get_all_ticks(self): """ Get all tick data. """ return list(self.ticks.values()) def get_all_orders(self): """ Get all order data. """ return list(self.orders.values()) def get_all_trades(self): """ Get all trade data. """ return list(self.trades.values()) def get_all_positions(self): """ Get all position data. """ # return list(self.positions.values()) return self.position_manager.get_all_positions() def get_errors(self): return self.errors def get_new_error(self): return self.errors[-1] def get_all_contracts(self): """ Get all contract data. """ return list(self.contracts.values()) def get_all_active_orders(self, local_symbol: str = ""): if not local_symbol: return list(self.active_orders.values()) else: active_orders = [ order for order in self.active_orders.values() if order.local_symbol == local_symbol ] return active_orders @property def main_contract_list(self): """ 返回主力合约列表 """ result = [] for _ in self.main_contract_mapping.values(): x = sorted(_, key=lambda x: x.open_interest, reverse=True)[0] result.append(x.local_symbol) return result def get_contract_last_price(self, local_symbol): """ 获取合约的最新价格 """ return self.local_contract_price_mapping.get(local_symbol) def get_main_contract_by_code(self, code: str): """ 根据code取相应的主力合约 """ d = self.main_contract_mapping.get(code.upper(), None) if not d: return None else: now = sorted(d, key=lambda x: x.open_interest, reverse=True)[0] pre = sorted(d, key=lambda x: x.pre_open_interest, reverse=True)[0] if pre.local_symbol == now.local_symbol: return pre else: return now def clear_all(self): """ 为了避免数据越来越大,需要清空数据 :return: """ self.ticks.clear() self.orders.clear() self.trades.clear() self.positions.clear() self.contracts.clear() self.errors.clear() self.shared.clear() self.generators.clear() self.active_orders.clear()
class Account: """ 此账户可以作为一个基本的账户类进行使用, 但是不能作为完整一个App. 需要配合 Interface 提供实时价格和收盘价格进行使用. 详细参见函数API """ def __init__(self, interface, name=None): """ 核心账户 Args """ self.account_id = name if name is not None else uuid.uuid4() # 成交接口 self.interface = interface # 每日成交单信息 self.daily_life = defaultdict(AliasDayResult) # 合约乘数 self.size_map = {} # 每日下单限制 self.daily_limit = 20 self.pre_balance = 0 # 账户当前的日期 self.date = None self.count_statistics = 0 # 初始资金 self.initial_capital = 0 # 账户权益 # 保证金占用 self.long_margin = 0 self.short_margin = 0 # 冻结的保证金 self.long_frozen_margin = {} self.short_frozen_margin = {} # 冻结的手续费 self.frozen_fee = {} # 多头净值 self.long_balance = 0 # 空头净值 self.short_balance = 0 self.frozen_premium = 0 self.count = 0 """ fee应该是一个 { ag2012.SHFE: 200.1 } """ self.fee = {} self.init_position_manager_flag = False self.init = False self.position_manager = None self.margin_ratio = {} # commission_ratio 应该为{"ag2012.SHFE": {"close_today": 0.005, "close":0.005 } self.basic_info = None self.commission_ratio = defaultdict(dict) self.close_profit = {} self.turnover = 0 self.pre_float = 0 self.position_manager = LocalPositionManager(self) self.code_pnl = {} @property def margin(self): result = 0 for x in self.position_manager.get_all_positions(): result += x["price"] * x["volume"] * self.get_size_from_map( x["local_symbol"]) * self.get_margin_ration( x["local_symbol"]) return result @property def frozen_margin(self): return sum(list(self.long_frozen_margin.values())) + sum(list(self.short_frozen_margin.values())) @property def to_object(self) -> AccountData: return AccountData._create_class(dict(accountid=self.account_id, local_account_id=f"{self.account_id}.SIM", frozen=self.frozen, balance=self.balance, )) @property def frozen(self): return sum(self.frozen_fee.values()) @property def float_pnl(self): result = 0 for pos in self.position_manager.get_all_positions(): if pos['direction'] == "long": result += (self.interface.price_mapping[pos["local_symbol"]] - pos[ "price"]) * pos["volume"] * self.get_size_from_map( pos["local_symbol"]) else: result += (pos["price"] - self.interface.price_mapping[ pos["local_symbol"]]) * pos["volume"] * self.get_size_from_map( pos["local_symbol"]) return result def get_code_float_pnl(self): """ 获取每个品种当时的浮动盈亏 """ result = {} for pos in self.position_manager.get_all_positions(): if pos['direction'] == "long": pnl = (self.interface.price_mapping[pos["local_symbol"]] - pos[ "price"]) * pos["volume"] * self.get_size_from_map( pos["local_symbol"]) else: pnl = (pos["price"] - self.interface.price_mapping[ pos["local_symbol"]]) * pos["volume"] * self.get_size_from_map( pos["local_symbol"]) if result.get(pos["local_symbol"]) is None: result[pos["local_symbol"]] = pnl else: result[pos["local_symbol"]] += pnl return result def get_code_pnl(self): pnl = {} pnl.update(self.code_pnl) for code, profit in self.get_code_float_pnl().items(): if code in pnl: pnl[code] += profit else: pnl[code] = profit return pnl @property def balance(self) -> float: return self.available + self.margin @property def available(self): """ 可用资金 = 前日权益 + 平仓盈亏 + 浮动盈亏 - 手续费 - 冻结手续费 - 保证金 """ return self.pre_balance + sum(self.close_profit.values()) + self.float_pnl - sum( self.fee.values()) - self.frozen - self.margin - self.frozen_margin @property def logger(self): return self.interface.logger def get_margin_ration(self, local_symbol): """ 获取保证金比率""" if self.basic_info is not None: if "." in local_symbol: local_symbol = local_symbol.split(".")[0] lc = "".join(filter(str.isalpha, local_symbol)) return self.basic_info[lc][self.interface.date].margin else: return self.margin_ratio.get(local_symbol) def get_commission(self, order: OrderData or TradeData, close_today=False): """ 获取手续费比率 """ if self.basic_info is not None: if "." in order.local_symbol: local_symbol = order.local_symbol.split(".")[0] else: local_symbol = order.local_symbol lc = "".join(filter(str.isalpha, local_symbol)) n = self.basic_info[lc][self.interface.date] # """ 切换到下一个交易日 """ if n.commission_type == 2: if close_today: """ 平今 """ return n.close_today_ratio * order.price * order.volume * self.get_size_from_map(order.local_symbol) else: return n.close_ratio * order.price * order.volume * self.get_size_from_map(order.local_symbol) else: if close_today: return n.close_today_ratio * order.volume else: return n.close_ratio * order.volume else: if close_today: return self.commission_ratio.get(order.local_symbol)[ "close_today"] * order.volume * order.price * self.get_size_from_map(order.local_symbol) else: return self.commission_ratio.get(order.local_symbol)[ "close"] * order.volume * order.price * self.get_size_from_map(order.local_symbol) def get_size_from_map(self, local_symbol): """ 获取合约乘数 """ if self.basic_info is not None: if "." in local_symbol: local_symbol = local_symbol.split(".")[0] lc = "".join(filter(str.isalpha, local_symbol)) return self.basic_info[lc][self.interface.date].size else: return self.size_map.get(local_symbol) def update_account_from_trade(self, data: TradeData or OrderData): """ 更新基础属性方法 # 下单更新冻结的保证金 # 成交更新持仓的保证金 开仓手续费 /平仓手续费 平今手续费 """ if isinstance(data, TradeData): """ 成交属性 """ if data.order_id in self.frozen_fee.keys(): # 如果已经成交那么清除手续费冻结.. self.frozen_fee.pop(data.order_id) try: if data.offset == Offset.CLOSETODAY: fee = self.get_commission(data, close_today=True) else: fee = self.get_commission(data) except KeyError: raise ValueError("请在对应品种设置合理的手续费") if self.fee.get(data.local_symbol) is None: self.fee[data.local_symbol] = fee else: self.fee[data.local_symbol] += fee if data.offset == Offset.OPEN: """ 开仓增加保证金 """ self.count += data.volume if data.direction == Direction.LONG: if data.order_id in self.long_frozen_margin.keys(): # 如果成交, 那么清除多头的保证金冻结 self.long_frozen_margin.pop(data.order_id) else: if data.order_id in self.short_frozen_margin.keys(): # 如果成交, 那么清除空头的保证金冻结 self.short_frozen_margin.pop(data.order_id) self.turnover += data.volume * data.price else: if data.direction == Direction.LONG: pos = self.position_manager.get_position_by_ld(data.local_symbol, Direction.SHORT) try: assert pos.volume >= data.volume except Exception: print(pos.volume, data.volume) raise ValueError close_profit = (pos.price - data.price) * data.volume * self.get_size_from_map(data.local_symbol) else: pos = self.position_manager.get_position_by_ld(data.local_symbol, Direction.LONG) assert pos.volume >= data.volume close_profit = (data.price - pos.price) * data.volume * self.get_size_from_map(data.local_symbol) if self.close_profit.get(data.local_symbol) is None: self.close_profit[data.local_symbol] = close_profit else: self.close_profit[data.local_symbol] += close_profit if self.code_pnl.get(data.local_symbol) is None: self.code_pnl[data.local_symbol] = close_profit else: self.code_pnl[data.local_symbol] += close_profit else: raise TypeError("错误的数据类型,期望成交单数据 TradeData 而不是 {}".format(type(data))) def update_account_from_order(self, order: OrderData): """ 从order里面更新订单信息 """ if order.offset == Offset.CLOSETODAY: self.frozen_fee[order.order_id] = self.get_commission(order, close_today=True) else: self.frozen_fee[order.order_id] = self.get_commission(order) if order.offset == Offset.OPEN: """ 开仓增加保证金占用 """ if order.direction == Direction.LONG: self.long_frozen_margin[order.order_id] = self.get_margin_ration( order.local_symbol) * order.price * order.volume * self.get_size_from_map(order.local_symbol) else: self.short_frozen_margin[order.order_id] = self.get_margin_ration( order.local_symbol) * order.price * order.volume * self.get_size_from_map(order.local_symbol) self.position_manager.update_order(order) def pop_order(self, order: OrderData): if order.direction == Direction.LONG: if order.order_id in self.long_frozen_margin: self.long_frozen_margin.pop(order.order_id) if order.direction == Direction.SHORT: if order.order_id in self.short_frozen_margin: self.short_frozen_margin.pop(order.order_id) if order.order_id in self.frozen_fee: self.frozen_fee.pop(order.order_id) def clear_frozen(self): """ 撤单的时候应该要清除所有的单子 并同时清除保证金占用和手续费冻结 """ from ctpbee.constant import EVENT_ORDER, Status # for order in list(self.interface.pending.values()): # """ 结算后需要把未所有的单子撤掉 """ # order.status = Status.CANCELLED # order.time.hour = 15 # order.time.minute = 1 # self.interface.on_event(EVENT_ORDER, order) self.interface.pending.clear() self.frozen_fee.clear() self.long_frozen_margin.clear() self.short_frozen_margin.clear() self.position_manager.clear_frozen() def reset_attr(self): """ 重新设置属性 """ self.frozen_premium = 0 self.count = 0 self.turnover = 0 self.close_profit.clear() self.code_pnl.clear() self.interface.today_volume = 0 for x in self.fee.keys(): self.fee[x] = 0 def close_position_by_amount(self, amount, price_mapping): """ 通过指定金额来金额来平仓直到available为正 """ self.logger.info(f"{self.date} 正在按照指定金额进行平仓: {amount}") for position in self.position_manager.get_all_positions(): margin = position["price"] * position["volume"] * self.get_size_from_map( position["local_symbol"]) * self.get_margin_ration(position["local_symbol"]) if margin > amount: volume = amount / (position["price"] * self.get_size_from_map( position["local_symbol"]) * self.get_margin_ration(position["local_symbol"])) if volume % 1 != 0: volume = int(volume) + 1 else: volume = int(volume) amount = 0 else: volume = position["volume"] amount -= margin if position["direction"] == "long": self.interface.action.cover(price_mapping[position["local_symbol"]], volume, position) else: self.interface.action.sell(price_mapping[position["local_symbol"]], volume, position) if amount <= 0: self.logger.info("已经发完足够包含指定保证金的平仓单") break if amount > 0: raise ValueError(f"你爆仓了!!!!, 什么策略????? 你的保证金: {self.margin} 可用:{self.available}") def is_traded(self, order: OrderData) -> bool: """ 当前账户是否足以支撑成交 """ # 根据传入的单子判断当前的账户可用资金是否足以成交此单 if order.offset != Offset.OPEN: """ 交易是否可平不足? """ poss = self.position_manager.get_position(order.local_symbol) if not poss: return False, "此合约上无仓位" if order.direction == Direction.LONG: if order.volume > poss.short_pos: """ 仓位不足 """ return False, f"平空头仓位不足 {order.local_symbol} volume: {order.volume} 持仓量: {poss.short_pos}" else: return True, None else: if order.volume > poss.long_pos: return False, f"平多头仓位不足 {order.local_symbol} volume: {order.volume} 持仓量: {poss.long_pos}" else: return True, None order_amount = order.price * order.volume * self.get_size_from_map( order.local_symbol) * self.get_margin_ration( order.local_symbol) if self.available < order_amount or self.available < 0: """ 可用不足""" return False, f"资金可用不足, 当前可用: {self.available} 当前冻结保证金: {self.frozen_margin}" return True, None def update_trade(self, trade: TradeData) -> None: """ 当前选择调用这个接口的时候就已经确保了这个单子是可以成交的, make sure it can be traded if you choose to call this method, :param trade:交易单子/trade :return: """ self.update_account_from_trade(trade) self.position_manager.update_trade(trade=trade) tr = "" assert trade.volume > 0 for pos in self.position_manager.get_all_positions(): if pos["direction"] == "long": tr += f"{pos['local_symbol']} long :{pos['volume']} \n" else: tr += f"{pos['local_symbol']} short :{pos['volume']} \n" if isinstance(trade.time, datetime): self.interface.position_detail[trade.time.strftime("%Y-%m-%d %H:%M:%S")] = tr elif isinstance(trade.time, str): self.interface.position_detail[trade.time] = tr def settle(self, interface_date=None): """ 生成今天的交易数据, 同时更新前日数据 ,然后进行持仓结算 """ if not self.date: date = interface_date else: date = self.date """ 结算撤掉所有单 归还冻结 """ self.clear_frozen() if self.available < 0: self.logger.info("你的可用不足, 进行减仓") self.close_position_by_amount(abs(self.available), self.interface.price_mapping) p = AliasDayResult( **{"balance": self.balance, "margin": self.margin, "available": self.available, "short_balance": self.short_balance, "long_balance": self.long_balance, "date": date, "commission": sum([x for x in self.fee.values()]), "net_pnl": self.balance - self.pre_balance, "count": self.count, "turnover": self.turnover }) code_pnl = self.get_code_pnl() self.interface.on_event(EVENT_WARNING, "Settlement: " + str( date) + f" net: {round(self.balance, 2)}" + f" margin: {round(self.margin, 2)}" f" net_pnl: {self.balance - self.pre_balance} " f" close_profit: {round(sum(self.close_profit.values()), 2)}" f" float_pnl: {round(self.float_pnl, 2)}" f" code_pnl: {code_pnl}" f" fee:{round(sum(self.fee.values()), 2)} ") self.pre_float = self.float_pnl self.daily_life[date] = deepcopy(p._to_dict()) self.pre_balance = self.balance self.long_balance = self.balance self.short_balance = self.balance self.reset_attr() self.position_manager.covert_to_yesterday_holding(**self.interface.price_mapping) # 归还所有的冻结 self.date = interface_date def via_aisle(self): self.position_manager.update_size_map(self.interface.params) if self.interface.date != self.date: self.date = self.interface.date else: pass def update_params(self, params: dict): """ 更新本地账户回测参数 """ for i, v in params.items(): if i == "initial_capital" and not self.init: self.pre_balance = v self.initial_capital = v self.long_balance = v self.short_balance = v self.init = True continue else: pass setattr(self, i, v) @property def result(self): # 根据daily_life里面的数据 获取最后的结果 result = defaultdict(list) for daily in self.daily_life.values(): for key, value in daily.items(): result[key].append(value) try: df = DataFrame.from_dict(result).set_index("date") except KeyError: print("-------------------------------------------------") print("| 好像没有结算数据哦! |") print("-------------------------------------------------") return {} try: import matplotlib.pyplot as plt df['balance'].plot() plt.show() except ImportError as e: pass finally: return self._cal_result(df) def get_mapping(self, d): mapping = {} for i, v in self.daily_life.items(): mapping[str(i)] = v.get(d) return mapping def _cal_result(self, df: DataFrame) -> dict: result = dict() df["return"] = (df["balance"] / df["balance"].shift(1) - 1).fillna(0) df["high_level"] = ( df["balance"].rolling( min_periods=1, window=len(df), center=False).max() ) df["draw_down"] = df["balance"] - df["high_level"] df["dd_percent"] = df["draw_down"] / df["high_level"] * 100 result['initial_capital / 初始资金'] = self.initial_capital result['start_date / 起始日期'] = df.index[0] result['end_date / 结束日期'] = df.index[-1] result['total_days / 交易天数'] = len(df) result['profit_days / 盈利天数'] = len(df[df["net_pnl"] > 0]) result['loss_days / 亏损天数'] = len(df[df["net_pnl"] < 0]) result['end_balance / 结束资金'] = round(df["balance"].iloc[-1], 2) result['max_draw_down / 最大回撤'] = round(df["draw_down"].min(), 2) result['max_dd_percent / 最大回撤百分比'] = str(round(df["dd_percent"].min(), 2)) + "%" result['total_pnl / 总盈亏'] = round(df["net_pnl"].sum(), 2) result['daily_pnl / 平均日盈亏'] = round(result['total_pnl / 总盈亏'] / result['total_days / 交易天数'], 2) result['total_commission / 总手续费'] = round(df["commission"].sum(), 2) result['daily_commission / 日均手续费'] = round(result['total_commission / 总手续费'] / result['total_days / 交易天数'], 2) result['total_turnover / 开仓总资金'] = round(df["turnover"].sum(), 2) result['daily_turnover / 每日平均开仓资金'] = round(result['total_turnover / 开仓总资金'] / result['total_days / 交易天数'], 2) result['total_count / 总成交次数'] = df["count"].sum() result['daily_count / 日均成交次数'] = round(result['total_count / 总成交次数'] / result['total_days / 交易天数'], 2) result['total_return / 总收益率'] = str( round((result['end_balance / 结束资金'] / self.initial_capital - 1) * 100, 2)) + "%" by_year_return_std = df["return"].std() * np.sqrt(245) df["return_x"] = df["return"] + 1 try: profit_ratio = geometric_mean(df["return_x"].to_numpy()) ** 245 - 1 except ValueError: print("boom 计算几何平均数存在负数, 本次回测作废") return {} result['annual_return / 年化收益率'] = str(round(profit_ratio * 100, 2)) + "%" result['return_std / 年化标准差'] = str(round(by_year_return_std * 100, 2)) + "%" result['volatility / 波动率'] = str(round(df["return"].std() * 100, 2)) + "%" if by_year_return_std != 0: result['sharpe / 年化夏普率'] = (profit_ratio - 2.5 / 100) / by_year_return_std else: result['sharpe / 年化夏普率'] = "计算出错" return result
def __init__(self, interface): self.interface = interface self.position_manager = LocalPositionManager(interface.params) # 每日资金情况 self.daily_life = defaultdict(AliasDayResult) self.date = None
class Account: """ 账户类 支持成交之后修改资金 , 对外提供API """ balance = 100000 frozen = 0 size = 5 pricetick = 10 daily_limit = 20 commission: float = 0 def __init__(self, interface): self.interface = interface self.position_manager = LocalPositionManager(interface.params) # 每日资金情况 self.daily_life = defaultdict(AliasDayResult) self.date = None def is_traded(self, trade: TradeData) -> bool: """ 当前账户是否足以支撑成交 """ # 根据传入的单子判断当前的账户资金和冻结 是否足以成交此单 if trade.price * trade.volume < self.balance - self.frozen: """ 可用不足""" return False return True def update_trade(self, trade: TradeData) -> None: """ 当前选择调用这个接口的时候就已经确保了这个单子是可以成交的, make sure it can be traded if you choose to call this method, :param trade:交易单子/trade_id :return: """ # 根据单子 更新当前的持仓 -----> if not self.date: self.date = self.interface.date if self.interface.date != self.date: """ 新的一天 """ p = AliasDayResult( **{ "balance": self.balance, "frozen": self.frozen, "available": self.balance - self.frozen }) self.daily_life[self.date] = p self.date = self.interface.date if self.commission != 0: commission_expense = trade.price * trade.volume else: commission_expense = 0 self.balance -= commission_expense # todo : 更新本地账户数据, 如果是隔天数据, 那么统计战绩 -----> AliasDayResult,然后推送到字典 。 以日期为key self.position_manager.update_trade(trade=trade) @property def result(self): # 计算并获取最后的结果 df = DataFrame.from_dict(self.daily_life).set_index("datetime") return df
class AsyncRecorder(object): """ data center """ def __init__(self, app, event_engine): """""" self.bar = {} self.ticks = {} self.orders = {} self.trades = {} self.positions = {} self.accounts = {} self.contracts = {} self.logs = {} self.errors = [] self.shared = {} self.generators = {} self.active_orders = {} self.event_engine = event_engine self.register_event() self.app = app self.position_manager = LocalPositionManager(app=self.app) @staticmethod def get_local_time(): from datetime import datetime return datetime.now().strftime('%Y-%m-%d %H:%M:%S') def register_event(self): """bind process function""" self.event_engine.register(EVENT_TICK, self.process_tick_event) self.event_engine.register(EVENT_ORDER, self.process_order_event) self.event_engine.register(EVENT_TRADE, self.process_trade_event) self.event_engine.register(EVENT_POSITION, self.process_position_event) self.event_engine.register(EVENT_ACCOUNT, self.process_account_event) self.event_engine.register(EVENT_CONTRACT, self.process_contract_event) self.event_engine.register(EVENT_BAR, self.process_bar_event) self.event_engine.register(EVENT_LOG, self.process_log_event) self.event_engine.register(EVENT_ERROR, self.process_error_event) self.event_engine.register(EVENT_SHARED, self.process_shared_event) async def process_shared_event(self, event): if self.shared.get(event.data.local_symbol, None) is not None: self.shared[event.data.local_symbol].append(event.data) else: self.shared[event.data.local_symbol] = [] for value in self.app.extensions.values(): await value(deepcopy(event)) async def process_error_event(self, event: Event): self.errors.append({"time": self.get_local_time(), "data": event.data}) print(self.get_local_time() + ": ", event.data) async def process_log_event(self, event: Event): self.logs[self.get_local_time()] = event.data if self.app.config.get("LOG_OUTPUT"): print(self.get_local_time() + ": ", event.data) for value in self.app.extensions.values(): await value(deepcopy(event)) async def process_bar_event(self, event: Event): bar = event.data local = self.bar.get(bar.local_symbol) if local is None: self.bar[bar.local_symbol] = {bar.interval: []} else: if self.bar[bar.local_symbol].get(bar.interval) is None: self.bar[bar.local_symbol] = {bar.interval: []} self.bar[bar.local_symbol][bar.interval].append(bar) for value in self.app.extensions.values(): await value(deepcopy(event)) async def process_tick_event(self, event: Event): """""" tick = event.data self.ticks[tick.local_symbol] = tick symbol = tick.symbol self.position_manager.update_tick(tick) # 生成datetime对象 if not tick.datetime: if '.' in tick.time: tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f') else: tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S') bm = self.generators.get(symbol, None) if bm: bm.update_tick(tick) if not bm: self.generators[symbol] = generator(self.event_engine) for value in self.app.extensions.values(): await value(deepcopy(event)) async def process_order_event(self, event: Event): """""" order = event.data self.orders[order.local_order_id] = order # If order is active, then update data in dict. if order._is_active(): self.active_orders[order.local_order_id] = order # Otherwise, pop inactive order from in dict elif order.local_order_id in self.active_orders: self.active_orders.pop(order.local_order_id) self.position_manager.update_order(order) for value in self.app.extensions.values(): await value(deepcopy(event)) async def process_trade_event(self, event: Event): """""" trade = event.data self.trades[trade.local_trade_id] = trade self.position_manager.update_trade(trade) for value in self.app.extensions.values(): await value(deepcopy(event)) async def process_position_event(self, event: Event): """""" position = event.data self.positions[position.local_position_id] = position self.position_manager.update_position(position) for value in self.app.extensions.values(): await value(deepcopy(event)) async def process_account_event(self, event: Event): """""" account = event.data self.accounts[account.local_account_id] = account for value in self.app.extensions.values(): await value(deepcopy(event)) async def process_contract_event(self, event: Event): """""" contract = event.data self.contracts[contract.local_symbol] = contract for value in self.app.extensions.values(): await value(deepcopy(event)) def get_shared(self, symbol): return self.shared.get(symbol, None) def get_all_shared(self): return self.shared def get_bar(self, local_symbol): return self.bar.get(local_symbol, None) def get_all_bar(self): return self.bar def get_tick(self, local_symbol): return self.ticks.get(local_symbol, None) def get_order(self, local_order_id): return self.orders.get(local_order_id, None) def get_trade(self, local_trade_id): return self.trades.get(local_trade_id, None) def get_position(self, local_position_id): return self.positions.get(local_position_id, None) def get_account(self, local_account_id): return self.accounts.get(local_account_id, None) def get_contract(self, local_symbol): return self.contracts.get(local_symbol, None) def get_all_ticks(self): """ Get all tick data. """ return list(self.ticks.values()) def get_all_orders(self): """ Get all order data. """ return list(self.orders.values()) def get_all_trades(self): """ Get all trade data. """ return list(self.trades.values()) def get_all_positions(self): """ Get all position data. """ return self.position_manager.get_all_positions() def get_errors(self): return self.errors def get_new_error(self): return self.errors[-1] def get_all_accounts(self): """ Get all account data. """ return list(self.accounts.values()) def get_all_contracts(self): """ Get all contract data. """ return list(self.contracts.values()) def get_all_active_orders(self, local_symbol: str = ""): if not local_symbol: return list(self.active_orders.values()) else: active_orders = [ order for order in self.active_orders.values() if order.local_symbol == local_symbol ] return active_orders
class Account: """ 账户类 支持成交之后修改资金 , 对外提供API """ def __init__(self): self.position_manager = LocalPositionManager(app=None) # 每日资金情况 self.daily_life = defaultdict(list) # 回测模式 self.pattern = "t+0" # 起始资金 默认10w 以及冻结 self.balance = 100000 self.frozen = 0 self.size = 5 self.pricetick = 10 self.daily_limit = 20 # 手续费 self.commission: float = 0 # 滑点相关设置 self.slip_page: float = 0 # 当前日志信息 self.date = None def is_traded(self, trade: TradeData) -> bool: """ 当前账户是否足以支撑成交 """ # 根据传入的单子判断当前的账户资金和冻结 是否足以成交此单 if trade.price * trade.volume < self.balance - self.frozen: """ 可用不足""" return False return True def update_trade(self, trade: TradeData) -> None: """ 当前选择调用这个接口的时候就已经确保了这个单子是可以成交的, make sure it can be traded if you choose to call this method, :param trade:交易单子/trade_id :return: """ # 根据单子 更新当前的持仓和-----> if trade.datetime.date != self.date: """ 新的一天 """ p = AliasDayResult({"balance": self.balance, "frozen": self.frozen, "avaiable": self.balance - self.frozen}) self.daily_life[self.date] = p self.date = trade.datetime.date commission_expense = trade.price * trade.volume self.balance -= commission_expense # todo : 更新本地账户数据, 如果是隔天数据, 那么统计战绩, -----> AliasDayResult,然后推送到字典 。 以日期为key self.position_manager.update_trade(trade=trade) @property def result(self): # 计算并获取最后的结果 df = DataFrame.from_dict(self.daily_life).set_index("datetime") return df