mirror of
https://github.com/microsoft/qlib.git
synced 2026-07-15 16:56:54 +08:00
black format
This commit is contained in:
@@ -22,7 +22,7 @@ def get_exchange(
|
||||
freq="day",
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
codes = "all",
|
||||
codes="all",
|
||||
subscribe_fields=[],
|
||||
open_cost=0.0015,
|
||||
close_cost=0.0025,
|
||||
@@ -89,6 +89,7 @@ def get_exchange(
|
||||
else:
|
||||
return init_instance_by_config(exchange, accept_types=Exchange)
|
||||
|
||||
|
||||
def init_env_instance_by_config(env):
|
||||
if isinstance(env, dict):
|
||||
env_config = copy.copy(env)
|
||||
@@ -103,6 +104,7 @@ def init_env_instance_by_config(env):
|
||||
else:
|
||||
return env
|
||||
|
||||
|
||||
def setup_exchange(root_instance, trade_exchange=None, force=False):
|
||||
if "trade_exchange" in inspect.getfullargspec(root_instance.__class__).args:
|
||||
if force:
|
||||
@@ -114,8 +116,8 @@ def setup_exchange(root_instance, trade_exchange=None, force=False):
|
||||
setup_exchange(root_instance.sub_env, trade_exchange)
|
||||
if hasattr(root_instance, "sub_strategy"):
|
||||
setup_exchange(root_instance.sub_strategy, trade_exchange)
|
||||
|
||||
|
||||
|
||||
|
||||
def backtest(start_time, end_time, strategy, env, benchmark=None, account=1e9, **kwargs):
|
||||
trade_strategy = init_instance_by_config(strategy)
|
||||
trade_env = init_env_instance_by_config(env)
|
||||
|
||||
@@ -11,7 +11,6 @@ from .order import Order
|
||||
from ...utils import parse_freq, sample_feature
|
||||
|
||||
|
||||
|
||||
"""
|
||||
rtn & earning in the Account
|
||||
rtn:
|
||||
@@ -87,7 +86,7 @@ class Account:
|
||||
elif norm_freq == "minute":
|
||||
_temp_result = D.features(_codes, fields, start_time, end_time, freq="minute", disk_cache=1)
|
||||
else:
|
||||
raise ValueError(f"benchmark freq {freq} is not supported")
|
||||
raise ValueError(f"benchmark freq {freq} is not supported")
|
||||
if len(_temp_result) == 0:
|
||||
raise ValueError(f"The benchmark {_codes} does not exist. Please provide the right benchmark")
|
||||
return _temp_result.groupby(level="datetime")[_temp_result.columns.tolist()[0]].mean().fillna(0)
|
||||
@@ -95,20 +94,20 @@ class Account:
|
||||
def _sample_benchmark(self, bench, trade_start_time, trade_end_time):
|
||||
def cal_change(x):
|
||||
return x.prod() - 1
|
||||
|
||||
return sample_feature(bench, trade_start_time, trade_end_time, method=cal_change)
|
||||
|
||||
def reset(self, benchmark=None, freq=None,**kwargs):
|
||||
def reset(self, benchmark=None, freq=None, **kwargs):
|
||||
if benchmark:
|
||||
self.benchmark = benchmark
|
||||
if freq:
|
||||
self.freq = freq
|
||||
if self.freq and self.benchmark and (freq or benchmark)
|
||||
if self.freq and self.benchmark and (freq or benchmark):
|
||||
self.bench = self._cal_benchmark(self.benchmark, self.start_time, self.end_time, self.freq)
|
||||
|
||||
for k, v in kwargs:
|
||||
if hasattr(k):
|
||||
setattr(k, v)
|
||||
|
||||
|
||||
def get_positions(self):
|
||||
return self.positions
|
||||
@@ -203,7 +202,7 @@ class Account:
|
||||
turnover_rate=self.to / last_account_value,
|
||||
cost_rate=self.ct / last_account_value,
|
||||
stock_value=now_stock_value,
|
||||
bench_value=self._sample_benchmark(self.bench, trade_start_time, trade_end_time)
|
||||
bench_value=self._sample_benchmark(self.bench, trade_start_time, trade_end_time),
|
||||
)
|
||||
# set now_account_value to position
|
||||
self.current.position["now_account_value"] = now_account_value
|
||||
|
||||
@@ -7,6 +7,7 @@ import pandas as pd
|
||||
|
||||
from .account import Account
|
||||
|
||||
|
||||
def backtest(start_time, end_time, trade_strategy, trade_env, benchmark, account):
|
||||
|
||||
trade_account = Account(init_cash=account, benchmark=benchmark, start_time=start_time, end_time=end_time)
|
||||
@@ -17,10 +18,9 @@ def backtest(start_time, end_time, trade_strategy, trade_env, benchmark, account
|
||||
while not trade_env.finished():
|
||||
_order_list = trade_strategy.generate_order_list(**trade_state)
|
||||
trade_state, trade_info = trade_env.execute(_order_list)
|
||||
|
||||
|
||||
report_df = trade_account.report.generate_report_dataframe()
|
||||
positions = trade_account.get_positions()
|
||||
report_dict = {"report_df": report_df, "positions": positions}
|
||||
|
||||
return report_dict
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
import re
|
||||
import json
|
||||
import copy
|
||||
@@ -14,15 +12,8 @@ from .report import Report
|
||||
from .order import Order
|
||||
|
||||
|
||||
|
||||
class BaseTradeCalendar:
|
||||
def __init__(
|
||||
self,
|
||||
step_bar,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
**kwargs
|
||||
):
|
||||
def __init__(self, step_bar, start_time=None, end_time=None, **kwargs):
|
||||
self.step_bar = step_bar
|
||||
self.reset(start_time=start_time, end_time=end_time)
|
||||
|
||||
@@ -36,8 +27,10 @@ class BaseTradeCalendar:
|
||||
if self.start_time and self.end_time:
|
||||
_calendar, freq, freq_sam = get_sample_freq_calendar(freq=self.step_bar)
|
||||
self.calendar = _calendar
|
||||
_start_time, _end_time, _start_index, _end_index = Cal.locate_index(self.start_time, self.end_time, freq=freq, freq_sam=freq_sam)
|
||||
_trade_calendar = self.calendar[_start_index: _end_index + 1]
|
||||
_start_time, _end_time, _start_index, _end_index = Cal.locate_index(
|
||||
self.start_time, self.end_time, freq=freq, freq_sam=freq_sam
|
||||
)
|
||||
_trade_calendar = self.calendar[_start_index : _end_index + 1]
|
||||
self.start_index = _start_index
|
||||
self.end_index = _end_index
|
||||
self.trade_len = _end_index - _start_index + 1
|
||||
@@ -52,7 +45,7 @@ class BaseTradeCalendar:
|
||||
for k, v in kwargs:
|
||||
if hasattr(self, k):
|
||||
setattr(self, k, v)
|
||||
|
||||
|
||||
def _get_calendar_time(self, trade_index=1, shift=0):
|
||||
trade_index = trade_index - shift
|
||||
calendar_index = self.start_index + trade_index
|
||||
@@ -64,6 +57,7 @@ class BaseTradeCalendar:
|
||||
def step(self):
|
||||
self.trade_index = self.trade_index + 1
|
||||
|
||||
|
||||
class BaseEnv(BaseTradeCalendar):
|
||||
"""
|
||||
# Strategy framework document
|
||||
@@ -83,8 +77,10 @@ class BaseEnv(BaseTradeCalendar):
|
||||
):
|
||||
self.generate_report = update_report
|
||||
self.verbose = verbose
|
||||
super(BaseEnv, self).__init__(step_bar=step_bar, start_time=start_time, end_time=end_time, trade_account=trade_account, **kwargs)
|
||||
|
||||
super(BaseEnv, self).__init__(
|
||||
step_bar=step_bar, start_time=start_time, end_time=end_time, trade_account=trade_account, **kwargs
|
||||
)
|
||||
|
||||
def reset(self, trade_account=None, **kwargs):
|
||||
super(BaseEnv, self).reset(**kwargs)
|
||||
if trade_account:
|
||||
@@ -94,7 +90,7 @@ class BaseEnv(BaseTradeCalendar):
|
||||
def get_init_state(self):
|
||||
init_state = {"current": self.trade_account.current}
|
||||
return init_state
|
||||
|
||||
|
||||
def execute(self, **kwargs):
|
||||
raise NotImplementedError("execute is not implemented!")
|
||||
|
||||
@@ -104,23 +100,32 @@ class BaseEnv(BaseTradeCalendar):
|
||||
def get_report(self):
|
||||
raise NotImplementedError("get_report is not implemented!")
|
||||
|
||||
|
||||
class SplitEnv(BaseEnv):
|
||||
def __init__(
|
||||
self,
|
||||
step_bar,
|
||||
self,
|
||||
step_bar,
|
||||
sub_env,
|
||||
sub_strategy,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
trade_account=None,
|
||||
update_report=False,
|
||||
verbose=False,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
):
|
||||
self.sub_env = sub_env
|
||||
self.sub_strategy = sub_strategy
|
||||
super(SplitEnv, self).__init__(step_bar=step_bar, start_time=start_time, end_time=end_time, trade_account=trade_account, update_report=update_report, verbose=verbose, **kwargs)
|
||||
|
||||
super(SplitEnv, self).__init__(
|
||||
step_bar=step_bar,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
trade_account=trade_account,
|
||||
update_report=update_report,
|
||||
verbose=verbose,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def reset(self, trade_account=None, **kwargs):
|
||||
super(SplitEnv, self).reset(trade_account=trade_account, **kwargs)
|
||||
if trade_account:
|
||||
@@ -129,9 +134,9 @@ class SplitEnv(BaseEnv):
|
||||
def execute(self, order_list, **kwargs):
|
||||
if self.finished():
|
||||
raise StopIteration(f"this env has completed its task, please reset it if you want to call it!")
|
||||
#if self.track:
|
||||
# if self.track:
|
||||
# yield action
|
||||
#episode_reward = 0
|
||||
# episode_reward = 0
|
||||
super(SplitEnv, self).step()
|
||||
trade_start_time, trade_end_time = self._get_calendar_time(self.trade_index)
|
||||
self.sub_env.reset(start_time=trade_start_time, end_time=trade_end_time)
|
||||
@@ -140,9 +145,11 @@ class SplitEnv(BaseEnv):
|
||||
while not self.sub_env.finished():
|
||||
_order_list = self.sub_strategy.generate_order_list(**trade_state)
|
||||
trade_state, trade_info = self.sub_env.execute(order_list=_order_list)
|
||||
|
||||
|
||||
if self.generate_report:
|
||||
self.trade_account.update_report(trade_start_time=trade_start_time, trade_end_time=trade_end_time, trade_exchange=self.trade_exchange)
|
||||
self.trade_account.update_report(
|
||||
trade_start_time=trade_start_time, trade_end_time=trade_end_time, trade_exchange=self.trade_exchange
|
||||
)
|
||||
_obs = {"current": self.trade_account.current}
|
||||
_info = {}
|
||||
return _obs, _info
|
||||
@@ -150,31 +157,40 @@ class SplitEnv(BaseEnv):
|
||||
def get_report(self):
|
||||
_report = self.trade_account.report.generate_report_dataframe() if self.generate_report else None
|
||||
_positions = self.trade_account.get_positions() if self.generate_report else None
|
||||
return [(_report,_positions), *sub_env.get_report()]
|
||||
|
||||
class SimulatorEnv(BaseEnv):
|
||||
return [(_report, _positions), *sub_env.get_report()]
|
||||
|
||||
|
||||
class SimulatorEnv(BaseEnv):
|
||||
def __init__(
|
||||
self,
|
||||
step_bar,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
trade_account=None,
|
||||
self,
|
||||
step_bar,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
trade_account=None,
|
||||
trade_exchange=None,
|
||||
update_report=False,
|
||||
verbose=False,
|
||||
**kwargs,
|
||||
):
|
||||
super(SimulatorEnv, self).__init__(step_bar=step_bar, start_time=start_time, end_time=end_time, trade_account=trade_account, trade_exchange=trade_exchange, update_report=update_report, verbose=verbose, **kwargs)
|
||||
super(SimulatorEnv, self).__init__(
|
||||
step_bar=step_bar,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
trade_account=trade_account,
|
||||
trade_exchange=trade_exchange,
|
||||
update_report=update_report,
|
||||
verbose=verbose,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def reset(self, trade_exchange=None, **kwargs):
|
||||
super(SimulatorEnv, self).reset(**kwargs)
|
||||
if trade_exchange:
|
||||
self.trade_exchange=trade_exchange
|
||||
self.trade_exchange = trade_exchange
|
||||
|
||||
def execute(self, order_list, **kwargs):
|
||||
"""
|
||||
Return: obs, done, info
|
||||
Return: obs, done, info
|
||||
"""
|
||||
if self.finished():
|
||||
raise StopIteration(f"this env has completed its task, please reset it if you want to call it!")
|
||||
@@ -184,7 +200,9 @@ class SimulatorEnv(BaseEnv):
|
||||
for order in order_list:
|
||||
if self.trade_exchange.check_order(order) is True:
|
||||
# execute the order
|
||||
trade_val, trade_cost, trade_price = self.trade_exchange.deal_order(order, trade_account=self.trade_account)
|
||||
trade_val, trade_cost, trade_price = self.trade_exchange.deal_order(
|
||||
order, trade_account=self.trade_account
|
||||
)
|
||||
trade_info.append((order, trade_val, trade_cost, trade_price))
|
||||
if self.verbose:
|
||||
if order.direction == Order.SELL: # sell
|
||||
@@ -214,7 +232,9 @@ class SimulatorEnv(BaseEnv):
|
||||
# do nothing
|
||||
pass
|
||||
if self.generate_report:
|
||||
self.trade_account.update_report(trade_start_time=trade_start_time, trade_end_time=trade_end_time, trade_exchange=self.trade_exchange)
|
||||
self.trade_account.update_report(
|
||||
trade_start_time=trade_start_time, trade_end_time=trade_end_time, trade_exchange=self.trade_exchange
|
||||
)
|
||||
_obs = {"current": self.trade_account.current}
|
||||
_info = {"trade_info": trade_info}
|
||||
return _obs, _info
|
||||
@@ -222,9 +242,4 @@ class SimulatorEnv(BaseEnv):
|
||||
def get_report(self):
|
||||
_report = self.trade_account.report.generate_report_dataframe() if self.generate_report else None
|
||||
_positions = self.trade_account.get_positions() if self.generate_report else None
|
||||
return [
|
||||
{
|
||||
"report": _report,
|
||||
"positions": _positions
|
||||
}
|
||||
]
|
||||
return [{"report": _report, "positions": _positions}]
|
||||
|
||||
@@ -16,7 +16,6 @@ from ...log import get_module_logger
|
||||
from .order import Order
|
||||
|
||||
|
||||
|
||||
class Exchange:
|
||||
def __init__(
|
||||
self,
|
||||
@@ -101,14 +100,15 @@ class Exchange:
|
||||
self.min_cost = min_cost
|
||||
self.limit_threshold = limit_threshold
|
||||
|
||||
|
||||
self.extra_quote = extra_quote
|
||||
self.set_quote(codes, start_time, end_time)
|
||||
|
||||
def set_quote(self, codes, start_time, end_time):
|
||||
if len(codes) == 0:
|
||||
codes = D.instruments()
|
||||
self.quote = D.features(codes, self.all_fields, start_time, end_time, freq=self.freq, disk_cache=True).dropna(subset=["$close"])
|
||||
self.quote = D.features(codes, self.all_fields, start_time, end_time, freq=self.freq, disk_cache=True).dropna(
|
||||
subset=["$close"]
|
||||
)
|
||||
self.quote.columns = self.all_fields
|
||||
|
||||
if self.quote[self.deal_price].isna().any():
|
||||
@@ -168,7 +168,6 @@ class Exchange:
|
||||
is limtited
|
||||
"""
|
||||
return sample_feature(self.quote[stock_id], start_time, end_time, fields="limit", method="all").iloc[0]
|
||||
|
||||
|
||||
def check_stock_suspended(self, stock_id, start_time, end_time):
|
||||
# is suspended
|
||||
@@ -180,7 +179,9 @@ class Exchange:
|
||||
def is_stock_tradable(self, stock_id, start_time, end_time):
|
||||
# check if stock can be traded
|
||||
# same as check in check_order
|
||||
if self.check_stock_suspended(stock_id, start_time, end_time) or self.check_stock_limit(stock_id, start_time, end_time):
|
||||
if self.check_stock_suspended(stock_id, start_time, end_time) or self.check_stock_limit(
|
||||
stock_id, start_time, end_time
|
||||
):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -235,9 +236,13 @@ class Exchange:
|
||||
return sample_feature(self.quote[stock_id], start_time, end_time, fields="$close", method="last").iloc[0]
|
||||
|
||||
def get_deal_price(self, stock_id, start_time, end_time):
|
||||
deal_price = sample_feature(self.quote[stock_id], start_time, end_time, fields=self.deal_price, method="last").iloc[0]
|
||||
deal_price = sample_feature(
|
||||
self.quote[stock_id], start_time, end_time, fields=self.deal_price, method="last"
|
||||
).iloc[0]
|
||||
if np.isclose(deal_price, 0.0) or np.isnan(deal_price):
|
||||
self.logger.warning(f"(stock_id:{stock_id}, trade_time:{(start_time, end_time)}, {self.deal_price}): {deal_price}!!!")
|
||||
self.logger.warning(
|
||||
f"(stock_id:{stock_id}, trade_time:{(start_time, end_time)}, {self.deal_price}): {deal_price}!!!"
|
||||
)
|
||||
self.logger.warning(f"setting deal_price to close price")
|
||||
deal_price = self.get_close(stock_id, start_time, end_time)
|
||||
return deal_price
|
||||
@@ -274,7 +279,9 @@ class Exchange:
|
||||
|
||||
amount_dict = {}
|
||||
for stock_id in weight_position:
|
||||
if weight_position[stock_id] > 0.0 and self.is_stock_tradable(stock_id=stock_id, start_time=start_time, end_time=end_time):
|
||||
if weight_position[stock_id] > 0.0 and self.is_stock_tradable(
|
||||
stock_id=stock_id, start_time=start_time, end_time=end_time
|
||||
):
|
||||
amount_dict[stock_id] = (
|
||||
cash
|
||||
* weight_position[stock_id]
|
||||
@@ -377,7 +384,10 @@ class Exchange:
|
||||
self.check_stock_suspended(stock_id=stock_id, start_time=start_time, end_time=end_time) is False
|
||||
and self.check_stock_limit(stock_id=stock_id, start_time=start_time, end_time=end_time) is False
|
||||
):
|
||||
value += self.get_deal_price(stock_id=stock_id, start_time=start_time, end_time=end_time) * amount_dict[stock_id]
|
||||
value += (
|
||||
self.get_deal_price(stock_id=stock_id, start_time=start_time, end_time=end_time)
|
||||
* amount_dict[stock_id]
|
||||
)
|
||||
return value
|
||||
|
||||
def round_amount_by_trade_unit(self, deal_amount, factor):
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
|
||||
class BaseInterpreter:
|
||||
@staticmethod
|
||||
def interpret(**kwargs):
|
||||
raise NotImplementedError("interpret is not implemented!")
|
||||
|
||||
|
||||
class ActionInterpreter:
|
||||
@staticmethod
|
||||
def interpret(action, **kwargs):
|
||||
return action
|
||||
|
||||
|
||||
class StateInterpreter:
|
||||
@staticmethod
|
||||
def interpret(state, **kwargs):
|
||||
return state
|
||||
return state
|
||||
|
||||
@@ -45,16 +45,7 @@ class Report:
|
||||
bench_value=None,
|
||||
):
|
||||
# check data
|
||||
if None in [
|
||||
trade_time,
|
||||
account_value,
|
||||
cash,
|
||||
return_rate,
|
||||
turnover_rate,
|
||||
cost_rate,
|
||||
stock_value,
|
||||
bench_value
|
||||
]:
|
||||
if None in [trade_time, account_value, cash, return_rate, turnover_rate, cost_rate, stock_value, bench_value]:
|
||||
raise ValueError(
|
||||
"None in [trade_date, account_value, cash, return_rate, turnover_rate, cost_rate, stock_value, bench_value]"
|
||||
)
|
||||
@@ -108,5 +99,5 @@ class Report:
|
||||
turnover_rate=r.loc[trade_time]["turnover"],
|
||||
cost_rate=r.loc[trade_time]["cost"],
|
||||
stock_value=r.loc[trade_time]["value"],
|
||||
bench_value=r.loc[trade_time]["bench"]
|
||||
bench_value=r.loc[trade_time]["bench"],
|
||||
)
|
||||
|
||||
@@ -7,12 +7,10 @@ from .model_strategy import (
|
||||
WeightStrategyBase,
|
||||
)
|
||||
|
||||
from .rule_strategy import(
|
||||
from .rule_strategy import (
|
||||
TWAPStrategy,
|
||||
SBBStrategyBase,
|
||||
SBBStrategyEMA,
|
||||
)
|
||||
|
||||
from .cost_control import (
|
||||
SoftTopkStrategy
|
||||
)
|
||||
from .cost_control import SoftTopkStrategy
|
||||
|
||||
@@ -53,7 +53,9 @@ class TopkDropoutStrategy(ModelStrategy):
|
||||
else:
|
||||
strategy will make decision with the tradable state of the stock info and avoid buy and sell them.
|
||||
"""
|
||||
super(TopkDropoutStrategy, self).__init__(step_bar, model, dataset, start_time, end_time, trade_exchange=trade_exchange)
|
||||
super(TopkDropoutStrategy, self).__init__(
|
||||
step_bar, model, dataset, start_time, end_time, trade_exchange=trade_exchange
|
||||
)
|
||||
self.topk = topk
|
||||
self.n_drop = n_drop
|
||||
self.method_sell = method_sell
|
||||
@@ -65,8 +67,7 @@ class TopkDropoutStrategy(ModelStrategy):
|
||||
self.stock_count = {}
|
||||
self.hold_thresh = hold_thresh
|
||||
self.only_tradable = only_tradable
|
||||
|
||||
|
||||
|
||||
def reset(self, trade_exchange=None, **kwargs):
|
||||
super(TopkDropoutStrategy, self).reset(**kwargs)
|
||||
if trade_exchange:
|
||||
@@ -94,7 +95,9 @@ class TopkDropoutStrategy(ModelStrategy):
|
||||
cur_n = 0
|
||||
res = []
|
||||
for si in reversed(l) if reverse else l:
|
||||
if self.trade_exchange.is_stock_tradable(stock_id=si, start_time=trade_start_time, end_time=trade_end_time):
|
||||
if self.trade_exchange.is_stock_tradable(
|
||||
stock_id=si, start_time=trade_start_time, end_time=trade_end_time
|
||||
):
|
||||
res.append(si)
|
||||
cur_n += 1
|
||||
if cur_n >= n:
|
||||
@@ -105,7 +108,13 @@ class TopkDropoutStrategy(ModelStrategy):
|
||||
return get_first_n(l, n, reverse=True)
|
||||
|
||||
def filter_stock(l):
|
||||
return [si for si in l if self.trade_exchange.is_stock_tradable(stock_id=si, start_time=trade_start_time, end_time=trade_end_time)]
|
||||
return [
|
||||
si
|
||||
for si in l
|
||||
if self.trade_exchange.is_stock_tradable(
|
||||
stock_id=si, start_time=trade_start_time, end_time=trade_end_time
|
||||
)
|
||||
]
|
||||
|
||||
else:
|
||||
# Otherwise, the stock will make decision with out the stock tradable info
|
||||
@@ -166,11 +175,16 @@ class TopkDropoutStrategy(ModelStrategy):
|
||||
buy_signal = pred_score.sort_values(ascending=False).iloc[: self.topk].index
|
||||
|
||||
for code in current_stock_list:
|
||||
if not self.trade_exchange.is_stock_tradable(stock_id=code, start_time=trade_start_time, end_time=trade_end_time):
|
||||
if not self.trade_exchange.is_stock_tradable(
|
||||
stock_id=code, start_time=trade_start_time, end_time=trade_end_time
|
||||
):
|
||||
continue
|
||||
if code in sell:
|
||||
# check hold limit
|
||||
if self.stock_count[code] < self.thresh or current_temp.get_stock_count(code, bar=self.step_bar) < self.hold_thresh:
|
||||
if (
|
||||
self.stock_count[code] < self.thresh
|
||||
or current_temp.get_stock_count(code, bar=self.step_bar) < self.hold_thresh
|
||||
):
|
||||
# can not sell this code
|
||||
# no buy signal, but the stock is kept
|
||||
self.stock_count[code] += 1
|
||||
@@ -188,7 +202,9 @@ class TopkDropoutStrategy(ModelStrategy):
|
||||
# is order executable
|
||||
if self.trade_exchange.check_order(sell_order):
|
||||
sell_order_list.append(sell_order)
|
||||
trade_val, trade_cost, trade_price = self.trade_exchange.deal_order(sell_order, position=current_temp)
|
||||
trade_val, trade_cost, trade_price = self.trade_exchange.deal_order(
|
||||
sell_order, position=current_temp
|
||||
)
|
||||
# update cash
|
||||
cash += trade_val - trade_cost
|
||||
# sold
|
||||
@@ -213,10 +229,14 @@ class TopkDropoutStrategy(ModelStrategy):
|
||||
# value = value / (1+self.trade_exchange.open_cost) # set open_cost limit
|
||||
for code in buy:
|
||||
# check is stock suspended
|
||||
if not self.trade_exchange.is_stock_tradable(stock_id=code, start_time=trade_start_time, end_time=trade_end_time):
|
||||
if not self.trade_exchange.is_stock_tradable(
|
||||
stock_id=code, start_time=trade_start_time, end_time=trade_end_time
|
||||
):
|
||||
continue
|
||||
# buy order
|
||||
buy_price = self.trade_exchange.get_deal_price(stock_id=code, start_time=trade_start_time, end_time=trade_end_time)
|
||||
buy_price = self.trade_exchange.get_deal_price(
|
||||
stock_id=code, start_time=trade_start_time, end_time=trade_end_time
|
||||
)
|
||||
buy_amount = value / buy_price
|
||||
factor = self.trade_exchange.get_factor(stock_id=code, start_time=trade_start_time, end_time=trade_end_time)
|
||||
buy_amount = self.trade_exchange.round_amount_by_trade_unit(buy_amount, factor)
|
||||
@@ -231,17 +251,24 @@ class TopkDropoutStrategy(ModelStrategy):
|
||||
buy_order_list.append(buy_order)
|
||||
self.stock_count[code] = 1
|
||||
return sell_order_list + buy_order_list
|
||||
|
||||
|
||||
|
||||
class WeightStrategyBase(ModelStrategy):
|
||||
def __init__(self, step_bar, start_time=None, end_time=None, order_generator_cls_or_obj=OrderGenWInteract, trade_exchange=None, **kwargs):
|
||||
def __init__(
|
||||
self,
|
||||
step_bar,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
order_generator_cls_or_obj=OrderGenWInteract,
|
||||
trade_exchange=None,
|
||||
**kwargs,
|
||||
):
|
||||
super(WeightStrategyBase, self).__init__(step_bar, start_time, end_time)
|
||||
self.trade_exchange = trade_exchange
|
||||
if isinstance(order_generator_cls_or_obj, type):
|
||||
self.order_generator = order_generator_cls_or_obj()
|
||||
else:
|
||||
self.order_generator = order_generator_cls_or_obj
|
||||
|
||||
|
||||
|
||||
def generate_target_weight_position(self, score, current, trade_start_time, trade_end_time):
|
||||
"""
|
||||
|
||||
@@ -81,10 +81,16 @@ class OrderGenWInteract(OrderGenerator):
|
||||
# calculate current_tradable_value
|
||||
current_amount_dict = current.get_stock_amount_dict()
|
||||
current_total_value = trade_exchange.calculate_amount_position_value(
|
||||
amount_dict=current_amount_dict, trade_start_time=trade_start_time, trade_end_time=trade_end_time, only_tradable=False
|
||||
amount_dict=current_amount_dict,
|
||||
trade_start_time=trade_start_time,
|
||||
trade_end_time=trade_end_time,
|
||||
only_tradable=False,
|
||||
)
|
||||
current_tradable_value = trade_exchange.calculate_amount_position_value(
|
||||
amount_dict=current_amount_dict, trade_start_time=trade_start_time, trade_end_time=trade_end_time, only_tradable=True
|
||||
amount_dict=current_amount_dict,
|
||||
trade_start_time=trade_start_time,
|
||||
trade_end_time=trade_end_time,
|
||||
only_tradable=True,
|
||||
)
|
||||
# add cash
|
||||
current_tradable_value += current.get_cash()
|
||||
@@ -97,7 +103,9 @@ class OrderGenWInteract(OrderGenerator):
|
||||
# value. Then just sell all the stocks
|
||||
target_amount_dict = copy.deepcopy(current_amount_dict.copy())
|
||||
for stock_id in list(target_amount_dict.keys()):
|
||||
if trade_exchange.is_stock_tradable(stock_id, trade_start_time=trade_start_time, trade_end_time=trade_end_time):
|
||||
if trade_exchange.is_stock_tradable(
|
||||
stock_id, trade_start_time=trade_start_time, trade_end_time=trade_end_time
|
||||
):
|
||||
del target_amount_dict[stock_id]
|
||||
else:
|
||||
# consider cost rate
|
||||
@@ -108,13 +116,13 @@ class OrderGenWInteract(OrderGenerator):
|
||||
target_amount_dict = trade_exchange.generate_amount_position_from_weight_position(
|
||||
weight_position=target_weight_position,
|
||||
cash=current_tradable_value,
|
||||
trade_start_time=trade_start_time,
|
||||
trade_start_time=trade_start_time,
|
||||
trade_end_time=trade_end_time,
|
||||
)
|
||||
order_list = trade_exchange.generate_order_for_target_amount_position(
|
||||
target_position=target_amount_dict,
|
||||
current_position=current_amount_dict,
|
||||
trade_start_time=trade_start_time,
|
||||
trade_start_time=trade_start_time,
|
||||
trade_end_time=trade_end_time,
|
||||
)
|
||||
return order_list
|
||||
@@ -161,7 +169,9 @@ class OrderGenWOInteract(OrderGenerator):
|
||||
amount_dict = {}
|
||||
for stock_id in target_weight_position:
|
||||
# Current rule will ignore the stock that not hold and cannot be traded at predict date
|
||||
if trade_exchange.is_stock_tradable(stock_id=stock_id, trade_start_time=trade_start_time, trade_end_time=trade_end_time):
|
||||
if trade_exchange.is_stock_tradable(
|
||||
stock_id=stock_id, trade_start_time=trade_start_time, trade_end_time=trade_end_time
|
||||
):
|
||||
amount_dict[stock_id] = (
|
||||
risk_total_value * target_weight_position[stock_id] / trade_exchange.get_close(stock_id, pred_date)
|
||||
)
|
||||
|
||||
@@ -11,7 +11,6 @@ from ..backtest.order import Order
|
||||
|
||||
|
||||
class TWAPStrategy(RuleStrategy, TradingEnhancement):
|
||||
|
||||
def reset(self, trade_order_list=None, **kwargs):
|
||||
super(TWAPStrategy, self).reset(**kwargs)
|
||||
TradingEnhancement.reset(self, trade_order_list=trade_order_list)
|
||||
@@ -19,7 +18,6 @@ class TWAPStrategy(RuleStrategy, TradingEnhancement):
|
||||
self.trade_amount = {}
|
||||
for order in self.trade_order_list:
|
||||
self.trade_amount[(order.stock_id, order.direction)] = order.amount // self.trade_len
|
||||
|
||||
|
||||
def generate_order_list(self, **kwargs):
|
||||
super(TopkDropoutStrategy, self).step()
|
||||
@@ -37,10 +35,12 @@ class TWAPStrategy(RuleStrategy, TradingEnhancement):
|
||||
order_list.append(_order)
|
||||
return order_list
|
||||
|
||||
|
||||
class SBBStrategyBase(RuleStrategy, TradingEnhancement):
|
||||
"""
|
||||
(S)elect the (B)etter one among every two adjacent trading (B)ars to sell or buy.
|
||||
(S)elect the (B)etter one among every two adjacent trading (B)ars to sell or buy.
|
||||
"""
|
||||
|
||||
TREND_MID = 0
|
||||
TREND_SHORT = 1
|
||||
TREND_LONG = 2
|
||||
@@ -50,11 +50,10 @@ class SBBStrategyBase(RuleStrategy, TradingEnhancement):
|
||||
TradingEnhancement.reset(self, trade_order_list=trade_order_list)
|
||||
if trade_order_list:
|
||||
self.trade_amount = {}
|
||||
self.trade_trend = {}
|
||||
self.trade_trend = {}
|
||||
for order in self.trade_order_list:
|
||||
self.trade_amount[(order.stock_id, order.direction)] = order.amount // self.trade_len
|
||||
self.trade_trend[(order.stock_id, order.direction)] = self.TREND_MID
|
||||
|
||||
|
||||
def _pred_price_trend(self, stock_id, pred_start_time=None, pred_end_time=None):
|
||||
raise NotImplementedError("pred_price_trend method is not implemented!")
|
||||
@@ -81,10 +80,15 @@ class SBBStrategyBase(RuleStrategy, TradingEnhancement):
|
||||
order_list.append(_order)
|
||||
else:
|
||||
if self.trade_index % 2 == 1:
|
||||
if _pred_trend == self.TREND_SHORT and order.direction == order.SELL or _pred_trend == self.TREND_LONG and order.direction == order.BUY:
|
||||
if (
|
||||
_pred_trend == self.TREND_SHORT
|
||||
and order.direction == order.SELL
|
||||
or _pred_trend == self.TREND_LONG
|
||||
and order.direction == order.BUY
|
||||
):
|
||||
_order = Order(
|
||||
stock_id=order.stock_id,
|
||||
amount=2*self.trade_amount[(order.stock_id, order.direction)],
|
||||
amount=2 * self.trade_amount[(order.stock_id, order.direction)],
|
||||
start_time=trade_start_time,
|
||||
end_time=trade_end_time,
|
||||
direction=order.direction, # 1 for buy
|
||||
@@ -92,31 +96,37 @@ class SBBStrategyBase(RuleStrategy, TradingEnhancement):
|
||||
)
|
||||
order_list.append(_order)
|
||||
else:
|
||||
if _pred_trend == self.TREND_SHORT and order.direction == order.BUY or _pred_trend == self.TREND_LONG and order.direction == order.SELL:
|
||||
if (
|
||||
_pred_trend == self.TREND_SHORT
|
||||
and order.direction == order.BUY
|
||||
or _pred_trend == self.TREND_LONG
|
||||
and order.direction == order.SELL
|
||||
):
|
||||
_order = Order(
|
||||
stock_id=order.stock_id,
|
||||
amount=2*self.trade_amount[(order.stock_id, order.direction)],
|
||||
amount=2 * self.trade_amount[(order.stock_id, order.direction)],
|
||||
start_time=trade_start_time,
|
||||
end_time=trade_end_time,
|
||||
direction=order.direction, # 1 for buy
|
||||
factor=order.factor,
|
||||
)
|
||||
)
|
||||
order_list.append(_order)
|
||||
if self.trade_index % 2 == 1:
|
||||
if self.trade_index % 2 == 1:
|
||||
self.trade_trend[(order.stock_id, order.direction)] = _pred_trend
|
||||
|
||||
return order_list
|
||||
|
||||
|
||||
|
||||
class SBBStrategyEMA(SBBStrategyBase):
|
||||
"""
|
||||
(S)elect the (B)etter one among every two adjacent trading (B)ars to sell or buy with (EMA).
|
||||
(S)elect the (B)etter one among every two adjacent trading (B)ars to sell or buy with (EMA).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
step_bar,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
self,
|
||||
step_bar,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
instruments="csi300",
|
||||
freq="day",
|
||||
**kwargs,
|
||||
@@ -139,22 +149,25 @@ class SBBStrategyEMA(SBBStrategyBase):
|
||||
if self.start_time and self.end_time:
|
||||
fields = ["EMA($close, 10)-EMA($close, 20)"]
|
||||
signal_start_time, _ = self._get_calendar_time(trade_index=self.trade_index, shift=1)
|
||||
signal_df = D.features(self.instruments, fields, start_time=signal_start_time, end_time=self.end_time, freq=self.freq)
|
||||
signal_df = D.features(
|
||||
self.instruments, fields, start_time=signal_start_time, end_time=self.end_time, freq=self.freq
|
||||
)
|
||||
signal_df = self._convert_index_format(signal_df)
|
||||
signal_df.columns = ["signal"]
|
||||
self.signal = {}
|
||||
for stock_id, stock_val in signal_df.groupby(level="instrument"):
|
||||
self.signal[stock_id] = stock_val
|
||||
|
||||
|
||||
def _pred_price_trend(self, stock_id, pred_start_time=None, pred_end_time=None):
|
||||
if stock_id not in self.signal:
|
||||
return self.TREND_MID
|
||||
else:
|
||||
_sample_signal = sample_feature(self.signal[stock_id], pred_start_time, pred_end_time, fields="signal", method="last")
|
||||
_sample_signal = sample_feature(
|
||||
self.signal[stock_id], pred_start_time, pred_end_time, fields="signal", method="last"
|
||||
)
|
||||
if _sample_signal is None or _sample_signal.iloc[0] == 0:
|
||||
return self.TREND_MID
|
||||
elif _sample_signal.iloc[0] > 0:
|
||||
return self.TREND_LONG
|
||||
else:
|
||||
return self.TREND_SHORT
|
||||
|
||||
Reference in New Issue
Block a user