You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
4.3 KiB
170 lines
4.3 KiB
"""
|
|
General utility functions.
|
|
"""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Callable
|
|
|
|
from .constant import Exchange
|
|
|
|
|
|
def extract_full_symbol(full_symbol: str):
|
|
"""
|
|
:return: (symbol, exchange)
|
|
"""
|
|
tmp = full_symbol.split(' ')
|
|
if len(tmp) < 4:
|
|
return "unknwonsymbol", Exchange.SHFE
|
|
symbol = tmp[2] + tmp[3]
|
|
exchange_str = tmp[0]
|
|
ex = Exchange(exchange_str)
|
|
if ex in [Exchange.SHFE, Exchange.DCE, Exchange.INE]:
|
|
symbol = symbol.lower()
|
|
return symbol, ex
|
|
|
|
|
|
# from ctp symbol to full symbol
|
|
def generate_full_symbol(exchange: Exchange, symbol: str, type: str = 'F'):
|
|
product = ''
|
|
contractno = ''
|
|
fullsym = symbol
|
|
if symbol:
|
|
if type == 'F' or type == 'O':
|
|
for count, word in enumerate(symbol):
|
|
if word.isdigit():
|
|
break
|
|
product = symbol[:count]
|
|
contractno = symbol[count:]
|
|
fullsym = exchange.value + ' ' + type + ' '\
|
|
+ product.upper() + ' ' + contractno
|
|
elif type == 'S':
|
|
combo = symbol.split(' ', 1)[1]
|
|
symbol1 = combo.split('&')[0]
|
|
symbol2 = combo.split('&')[1]
|
|
for count, word in enumerate(symbol1):
|
|
if word.isdigit():
|
|
break
|
|
product = symbol1[:count]
|
|
contractno = symbol1[count:]
|
|
for count, word in enumerate(symbol2):
|
|
if word.isdigit():
|
|
break
|
|
product += ('&' + symbol2[:count])
|
|
contractno += ('&' + symbol2[count:])
|
|
fullsym = exchange.value + ' ' + type + ' '\
|
|
+ product.upper() + ' ' + contractno
|
|
return fullsym
|
|
|
|
|
|
def extract_vt_symbol(vt_symbol: str):
|
|
"""
|
|
:return: (symbol, exchange)
|
|
"""
|
|
symbol, exchange_str = vt_symbol.split('.')
|
|
return symbol, Exchange(exchange_str)
|
|
|
|
|
|
def generate_vt_symbol(symbol: str, exchange: Exchange):
|
|
return f'{symbol}.{exchange.value}'
|
|
|
|
|
|
def _get_trader_dir(temp_name: str):
|
|
"""
|
|
Get path where trader is running in.
|
|
"""
|
|
cwd = Path.cwd()
|
|
temp_path = cwd.joinpath(temp_name)
|
|
|
|
# If .StarQuant folder exists in current working directory,
|
|
# then use it as trader running path.
|
|
if temp_path.exists():
|
|
return cwd, temp_path
|
|
|
|
# Otherwise use home path of system.
|
|
home_path = Path.home()
|
|
temp_path = home_path.joinpath(temp_name)
|
|
|
|
# Create .StarQuant folder under home path if not exist.
|
|
if not temp_path.exists():
|
|
temp_path.mkdir()
|
|
|
|
return home_path, temp_path
|
|
|
|
|
|
TRADER_DIR, TEMP_DIR = _get_trader_dir(".StarQuant")
|
|
|
|
|
|
def get_file_path(filename: str):
|
|
"""
|
|
Get path for temp file with filename.
|
|
"""
|
|
return TEMP_DIR.joinpath(filename)
|
|
|
|
|
|
def get_folder_path(folder_name: str):
|
|
"""
|
|
Get path for temp folder with folder name.
|
|
"""
|
|
folder_path = TEMP_DIR.joinpath(folder_name)
|
|
if not folder_path.exists():
|
|
folder_path.mkdir()
|
|
return folder_path
|
|
|
|
|
|
def get_icon_path(filepath: str, ico_name: str):
|
|
"""
|
|
Get path for icon file with ico name.
|
|
"""
|
|
ui_path = Path(filepath).parent
|
|
icon_path = ui_path.joinpath("ico", ico_name)
|
|
return str(icon_path)
|
|
|
|
|
|
def load_json(filename: str):
|
|
"""
|
|
Load data from json file in temp path.
|
|
"""
|
|
filepath = get_file_path(filename)
|
|
|
|
if filepath.exists():
|
|
with open(filepath, mode='r') as f:
|
|
data = json.load(f)
|
|
return data
|
|
else:
|
|
save_json(filename, {})
|
|
return {}
|
|
|
|
|
|
def save_json(filename: str, data: dict):
|
|
"""
|
|
Save data into json file in temp path.
|
|
"""
|
|
filepath = get_file_path(filename)
|
|
with open(filepath, mode='w+') as f:
|
|
json.dump(data, f, indent=4)
|
|
|
|
|
|
def round_to_pricetick(price: float, pricetick: float):
|
|
"""
|
|
Round price to price tick value.
|
|
"""
|
|
rounded = round(price / pricetick, 0) * pricetick
|
|
return rounded
|
|
|
|
|
|
def round_to(value: float, target: float):
|
|
"""
|
|
Round price to price tick value.
|
|
"""
|
|
rounded = int(round(value / target)) * target
|
|
return rounded
|
|
|
|
|
|
def virtual(func: Callable):
|
|
"""
|
|
mark a function as "virtual", which means that this function can be override.
|
|
any base class should use this or @abstractmethod to decorate all functions
|
|
that can be (re)implemented by subclasses.
|
|
"""
|
|
return func
|
|
|