Skip to content

Commit d2d5419

Browse files
committed
[DB] Remove Database Dependency
Signed-off-by: Bill Chan <[email protected]>
1 parent bc4a8b9 commit d2d5419

13 files changed

+29
-158
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,4 @@ cython_debug/
164164
!/filters/Triple_Cross.py
165165

166166
/backtesting_report/
167+
/stock_filter_report/

README.md

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,6 @@ Password_md5 = <Futu Login Password Md5 Value>
7373
HistoryDataFormat = ["code","time_key","open","close","high","low","pe_ratio","turnover_rate","volume","turnover","change_rate","last_close"]
7474
SubscribedDataFormat = None
7575

76-
[Database]
77-
Database_path = <Your SQLite Database File Path>
78-
7976
[TradePreference]
8077
LotSizeMultiplier = <# of Stocks to Buy per Signal>
8178
MaxPercPerAsset = <Maximum % of Capital Allocated per Asset>
@@ -127,20 +124,6 @@ to https://openapi.futunn.com/futu-api-doc/qa/quote.html
127124

128125
**MAKE SURE YOU LOGIN TO FUTU OPEND FIRST BEFORE STARTING FUTU_ALGO!**
129126

130-
### 3. Initialize SQLite Database
131-
132-
Go to [SQLite official website](https://www.sqlite.org/quickstart.html) and follow the QuickStart instruction to install
133-
SQLite tools in the device.
134-
135-
Create a folder named 'database' in the root folder, and execute the SQLite DDL file stored in *./util/database_ddl.sql*
136-
.
137-
138-
```
139-
./
140-
├── database
141-
│ └── stock_data.sqlite
142-
```
143-
144127
### 4. Download Data (e.g. 1M Data for max. 2 Years)
145128

146129
For **Windows**:
@@ -166,10 +149,6 @@ If you want to refresh all data, use the following command instead (WITH CAUTION
166149

167150
python main_backend.py -fu / python main_backend.py --force_update
168151

169-
Store all data from CSV to SQLite Database *(Currently the database isn't used for any feature)*
170-
171-
python main_backend.py -d / python main_backend.py --database
172-
173152
Execute High-Frequency Trading (HFT) with a Pre-defined Strategy
174153

175154
python main_backend.py -s MACD_Cross / python main_backend.py --strategy MACD_Cross

SECURITY.md

Lines changed: 0 additions & 21 deletions
This file was deleted.

config_template.ini

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ Password_md5 = 2134342ABC2D03780772038A7816
1313
HistoryDataFormat = ["code","time_key","open","close","high","low","pe_ratio","turnover_rate","volume","turnover","change_rate","last_close"]
1414
SubscribedDataFormat = None
1515

16-
[Database]
17-
Database_path = ./database/stock_data.sqlite
18-
1916
[TradePreference]
2017
LotSizeMultiplier = 2
2118
MaxPercPerAsset = 10

engines/data_engine.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333

3434
from util import logger
3535
from util.global_vars import *
36+
from deprecated import deprecated
3637

3738

39+
@deprecated(version='1.0', reason="Database dependency is removed.")
3840
class DatabaseInterface:
3941
def __init__(self, database_path):
4042
Path("./database/").mkdir(parents=True, exist_ok=True)

engines/stock_filter_engine.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
from datetime import date
2121
from multiprocessing import Pool, cpu_count
2222

23-
from engines.data_engine import DatabaseInterface, HKEXInterface, YahooFinanceInterface
23+
import pandas as pd
24+
25+
from engines.data_engine import HKEXInterface, YahooFinanceInterface
2426
from util import logger
2527
from util.global_vars import *
2628

@@ -87,20 +89,16 @@ def update_filtered_equity_pools(self):
8789
filtered_stock_list = pool.map(self.validate_stock_individual, self.full_equity_list)
8890
pool.close()
8991
pool.join()
90-
# filtered_stock_list = []
91-
# for stock_code in self.full_equity_list:
92-
# filtered_stock_list.append(self.validate_stock_individual(stock_code))
93-
94-
# Remove Redundant Records (If Exists)
95-
database = DatabaseInterface(database_path=self.config['Database'].get('Database_path'))
96-
database.delete_stock_pool_from_date(date.today().strftime("%Y-%m-%d"))
97-
database.commit()
92+
93+
filtered_stock_df = pd.DataFrame([], columns=['filter', 'code'])
94+
9895
# Flatten Nested List
9996
for sublist in filtered_stock_list:
10097
for record in sublist:
101-
database.add_stock_pool(date.today().strftime("%Y-%m-%d"), record[0], record[1])
98+
filtered_stock_df.append({'filter': record[0], 'code': record[1]})
10299
self.default_logger.info(f"Added Filtered Stock {record[1]} based on Filter {record[0]}")
103-
database.commit()
100+
filtered_stock_df.to_csv(PATH_FILTER_REPORT / f'{date.today().strftime("%Y-%m-%d")}_stock_list', index=False,
101+
encoding='utf-8-sig')
104102

105103
def parse_stock_info(self, stock_code):
106104
return stock_code, YahooFinanceInterface.get_stock_info(stock_code)

engines/trading_engine.py

Lines changed: 2 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import subprocess
2323
from datetime import date
2424

25+
from deprecated import deprecated
2526
from futu import *
2627

2728
import engines
@@ -45,8 +46,6 @@ def __init__(self):
4546
self.username = self.config['FutuOpenD.Credential'].get('Username')
4647
# self.password = self.config['FutuOpenD.Credential'].get('Password')
4748
self.password_md5 = self.config['FutuOpenD.Credential'].get('Password_md5')
48-
self.futu_data = engines.data_engine.DatabaseInterface(
49-
database_path=self.config['Database'].get('Database_path'))
5049
self.trd_env = TrdEnv.REAL if self.config.get('FutuOpenD.Config', 'TrdEnv') == 'REAL' else TrdEnv.SIMULATE
5150
self.trading_util = engines.TradingUtil(self.quote_ctx, self.trade_ctx, self.trd_env)
5251
# Futu-Specific Variables
@@ -135,65 +134,10 @@ def __save_historical_data(self, stock_code: str, start_date: date, end_date: da
135134
time.sleep(1)
136135
self.default_logger.error(f'Historical Data Store Error: {data}')
137136

138-
def __store_data_database(self, data, k_type):
139-
for index, row in data.iterrows():
140-
self.futu_data.add_stock_data(row['code'], row['time_key'], row['open'], row['close'], row['high'],
141-
row['low'], row['pe_ratio'], row['turnover_rate'], row['volume'],
142-
row['turnover'], row['change_rate'], row['last_close'], k_type)
143-
self.futu_data.commit()
144-
145137
def get_market_state(self):
146-
"""
147-
获取全局状态
148-
149-
:return: (ret, data)
150-
151-
ret == RET_OK data为包含全局状态的字典,含义如下
152-
153-
ret != RET_OK data为错误描述字符串
154-
155-
===================== =========== ==============================================================
156-
key value类型 说明
157-
===================== =========== ==============================================================
158-
market_sz str 深圳市场状态,参见MarketState
159-
market_us str 美国市场状态,参见MarketState
160-
market_sh str 上海市场状态,参见MarketState
161-
market_hk str 香港市场状态,参见MarketState
162-
market_hkfuture str 香港期货市场状态,参见MarketState
163-
market_usfuture str 美国期货市场状态,参见MarketState
164-
server_ver str FutuOpenD版本号
165-
trd_logined str '1':已登录交易服务器,'0': 未登录交易服务器
166-
qot_logined str '1':已登录行情服务器,'0': 未登录行情服务器
167-
timestamp str Futu后台服务器当前时间戳(秒)
168-
local_timestamp double FutuOpenD运行机器当前时间戳(
169-
===================== =========== ==============================================================
170-
"""
171138
return self.quote_ctx.get_global_state()
172139

173-
def get_referencestock_list(self, stock_code: str) -> pd.DataFrame:
174-
"""
175-
获取证券的关联数据
176-
:param code: 证券id,str,例如HK.00700
177-
:return: (ret, data)
178-
ret == RET_OK 返回pd dataframe数据,数据列格式如下
179-
180-
ret != RET_OK 返回错误字符串
181-
======================= =========== ==============================================================================
182-
参数 类型 说明
183-
======================= =========== ==============================================================================
184-
code str 证券代码
185-
lot_size int 每手数量
186-
stock_type str 证券类型,参见SecurityType
187-
stock_name str 证券名字
188-
list_time str 上市时间(美股默认是美东时间,港股A股默认是北京时间)
189-
wrt_valid bool 是否是窝轮,如果为True,下面wrt开头的字段有效
190-
wrt_type str 窝轮类型,参见WrtType
191-
wrt_code str 所属正股
192-
future_valid bool 是否是期货,如果为True,下面future开头的字段有效
193-
future_main_contract bool 是否主连合约(期货特有字段)
194-
future_last_trade_time string 最后交易时间(期货特有字段,非主连期货合约才有值)
195-
======================= =========== ==============================================================================
196-
"""
140+
def get_reference_stock_list(self, stock_code: str) -> pd.DataFrame:
197141
output_df = pd.DataFrame()
198142
for security_reference_type in self.security_type_list:
199143
ret, data = self.quote_ctx.get_referencestock_list(stock_code, security_reference_type)
@@ -398,35 +342,11 @@ def update_stock_basicinfo(self):
398342
output_df.to_csv(output_path, index=False, encoding='utf-8-sig')
399343
self.default_logger.info(f'Stock Static Basic Info Updated: {output_path}')
400344

401-
def store_all_data_database(self):
402-
"""
403-
Store all files in ./data/{stock_code}/*.csv to the database in pre-defined format.
404-
"""
405-
file_list = glob.glob(f"./data/*/*_1M.csv", recursive=True)
406-
for input_file in file_list:
407-
input_csv = pd.read_csv(input_file, index_col=None)
408-
self.default_logger.info(f'Saving to Database: {input_file}')
409-
self.__store_data_database(input_csv, k_type=KLType.K_1M)
410-
411-
file_list = glob.glob(f"./data/*/*_1D.csv", recursive=True)
412-
for input_file in file_list:
413-
input_csv = pd.read_csv(input_file, index_col=None)
414-
self.default_logger.info(f'Saving to Database: {input_file}')
415-
self.__store_data_database(input_csv, k_type=KLType.K_DAY)
416-
417-
file_list = glob.glob(f"./data/*/*_1W.csv", recursive=True)
418-
for input_file in file_list:
419-
input_csv = pd.read_csv(input_file, index_col=None)
420-
self.default_logger.info(f'Saving to Database: {input_file}')
421-
self.__store_data_database(input_csv, k_type=KLType.K_WEEK)
422-
423345
def cur_kline_evalaute(self, stock_list: list, strategy_map: dict, sub_type: SubType = SubType.K_1M):
424346
"""
425347
Real-Time K-Line!
426-
:param input_data: Dictionary in Format {'HK.00001': pd.Dataframe, 'HK.00002': pd.Dataframe}
427348
:param stock_list: A List of Stock Code with Format (e.g., [HK.00001, HK.00002])
428349
:param strategy_map: Strategies defined in ./strategies class. Should be inherited from based class Strategies
429-
:param timeout: Subscription Timeout in secs.
430350
:param sub_type: Subscription SubType for FuTu (i.e., Trading Frequency)
431351
432352
"""
File renamed without changes.

main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,6 @@ def resize_grips(self):
612612

613613
if __name__ == "__main__":
614614
app = QApplication(sys.argv)
615-
app.setWindowIcon(QtGui.QIcon("icon.ico"))
615+
app.setWindowIcon(QtGui.QIcon("images/icon.ico"))
616616
window = SplashScreen()
617617
sys.exit(app.exec_())

main_backend.py

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ def __daily_update_filters():
3535

3636
def daily_update_data(futu_trade, stock_list: list, force_update: bool = False):
3737
# Daily Update Filtered Security
38-
procs = []
39-
proc = Process(target=__daily_update_filters) # instantiating without any argument
40-
procs.append(proc)
41-
proc.start()
38+
# procs = []
39+
# proc = Process(target=__daily_update_filters) # instantiating without any argument
40+
# procs.append(proc)
41+
# proc.start()
4242

4343
# Daily Update Stock Info (Need to Rethink!!!)
4444
# stock_filter.update_stock_info()
@@ -57,16 +57,13 @@ def daily_update_data(futu_trade, stock_list: list, force_update: bool = False):
5757
for stock_code in stock_list:
5858
futu_trade.update_DW_data(stock_code, force_update=force_update, k_type=KLType.K_DAY)
5959
futu_trade.update_DW_data(stock_code, force_update=force_update, k_type=KLType.K_WEEK)
60-
futu_trade.update_1M_data(stock_code, force_update=force_update, default_days=80)
61-
62-
# Daily Update FuTu Historical Data
63-
# futu_trade.store_all_data_database()
60+
futu_trade.update_1M_data(stock_code, force_update=force_update, default_days=365)
6461

6562
# Clean non-trading days data
6663
DataProcessingInterface.clear_empty_data()
6764

68-
for proc in procs:
69-
proc.join()
65+
# for proc in procs:
66+
# proc.join()
7067

7168

7269
def __dynamic_instantiation(prefix: str, module_name: str, optional_parameter=None):
@@ -82,7 +79,7 @@ def __dynamic_instantiation(prefix: str, module_name: str, optional_parameter=No
8279
def __init_strategy(strategy_name: str, input_data: dict) -> Strategies:
8380
"""
8481
Return a trading strategy instance using a strategy name in string.
85-
:param strategy_name: an available strategy module name in the strategies folder
82+
:param strategy_name: an available strategy module name in the strategies' folder
8683
:param input_data: Initialized input data for the strategy to calculate the technical indicator
8784
:return: a strategy instance
8885
"""
@@ -147,7 +144,6 @@ def main():
147144
action="store_true")
148145
parser.add_argument("-fu", "--force_update",
149146
help="Force Update All Data Up to Max. Allowed Years (USE WITH CAUTION)", action="store_true")
150-
parser.add_argument("-d", "--database", help="Store All CSV Data to Database", action="store_true")
151147

152148
# Trading Related Arguments
153149
strategy_list = [Path(file_name).name[:-3] for file_name in glob.glob("./strategies/*.py") if
@@ -176,8 +172,6 @@ def main():
176172
# Initialize Stock List
177173
stock_list = json.loads(config.get('TradePreference', 'StockList'))
178174
if not stock_list:
179-
# stock_list = data_engine.DatabaseInterface(
180-
# database_path=config.get('Database', 'Database_path')).get_stock_list()
181175
# Directly get list of stock codes from the data folder. Easier.
182176
stock_list = [str(f.path).replace('./data/', '') for f in os.scandir("./data/") if f.is_dir()]
183177
stock_list = stock_list[:-1]
@@ -192,14 +186,11 @@ def main():
192186
if args.update or args.force_update:
193187
# Daily Update Data
194188
daily_update_data(futu_trade=futu_trade, stock_list=stock_list, force_update=args.force_update)
195-
if args.database:
196-
# Update ALl Data to Database
197-
futu_trade.store_all_data_database()
198189
if args.strategy:
199190
# Stock Basket => 4 Parts
200191
# 1. Currently Holding Stocks (i.e., in the trading account with existing position)
201192
# 2. Filtered Stocks (i.e., based on 1D data if -f option is adopted
202-
# 3. StockList in config.ini (i.e., if empty, default use all stocks in the database)
193+
# 3. StockList in config.ini (i.e., if empty, default use all stocks in the data folder)
203194
# 4. Top 30 HSI Constituents
204195
if args.filter:
205196
stock_list.extend(filtered_stock_list)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import pkg_resources
2323

2424
# ADD FILES
25-
files = ['icon.ico', 'themes/', 'stock_strategy_map_template.yml', 'config_template.ini']
25+
files = ['images/icon.ico', 'themes/', 'stock_strategy_map_template.yml', 'config_template.ini']
2626

2727
# TARGET
2828
target = Executable(
500 KB
Binary file not shown.

util/global_vars.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121

2222
import yaml
2323

24+
PATH = Path.cwd()
25+
PATH_DATA = PATH / 'data'
26+
PATH_FILTER_REPORT = PATH / 'stock_filter_report'
27+
2428
if not Path("config.ini").exists():
2529
raise SystemExit("Missing config.ini. Please use the config_template.ini to create your configuration.")
2630

0 commit comments

Comments
 (0)