diff --git a/v2realbot/common/model.py b/v2realbot/common/model.py index 8674b30..fc690b1 100644 --- a/v2realbot/common/model.py +++ b/v2realbot/common/model.py @@ -5,7 +5,7 @@ from rich import print from typing import Any, Optional, List, Union from datetime import datetime, date from pydantic import BaseModel, Field -from v2realbot.enums.enums import Mode, Account, SchedulerStatus, Moddus +from v2realbot.enums.enums import Mode, Account, SchedulerStatus, Moddus, Market from alpaca.data.enums import Exchange @@ -159,6 +159,7 @@ class RunManagerRecord(BaseModel): mode: Mode note: Optional[str] = None ilog_save: bool = False + market: Optional[Market] = Market.US bt_from: Optional[datetime] = None bt_to: Optional[datetime] = None #weekdays filter diff --git a/v2realbot/common/transform.py b/v2realbot/common/transform.py index 53ad582..bd17cfd 100644 --- a/v2realbot/common/transform.py +++ b/v2realbot/common/transform.py @@ -5,9 +5,7 @@ import v2realbot.controller.services as cs #prevede dict radku zpatky na objekt vcetme retypizace def row_to_runmanager(row: dict) -> RunManagerRecord: - is_running = cs.is_runner_running(row['runner_id']) if row['runner_id'] else False - res = RunManagerRecord( moddus=row['moddus'], id=row['id'], @@ -17,6 +15,7 @@ def row_to_runmanager(row: dict) -> RunManagerRecord: account=row['account'], note=row['note'], ilog_save=bool(row['ilog_save']), + market=row['market'] if row['market'] is not None else None, bt_from=datetime.fromisoformat(row['bt_from']) if row['bt_from'] else None, bt_to=datetime.fromisoformat(row['bt_to']) if row['bt_to'] else None, weekdays_filter=[int(x) for x in row['weekdays_filter'].split(',')] if row['weekdays_filter'] else [], diff --git a/v2realbot/controller/run_manager.py b/v2realbot/controller/run_manager.py index 06e06e7..db01ac5 100644 --- a/v2realbot/controller/run_manager.py +++ b/v2realbot/controller/run_manager.py @@ -172,14 +172,14 @@ def add_run_manager_record(new_record: RunManagerRecord): # Construct a suitable INSERT query based on your RunManagerRecord fields insert_query = """ INSERT INTO run_manager (moddus, id, strat_id, symbol,account, mode, note,ilog_save, - bt_from, bt_to, weekdays_filter, batch_id, + market, bt_from, bt_to, weekdays_filter, batch_id, start_time, stop_time, status, last_processed, history, valid_from, valid_to, testlist_id) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?) """ values = [ new_record.moddus, str(new_record.id), str(new_record.strat_id), new_record.symbol, new_record.account, new_record.mode, new_record.note, - int(new_record.ilog_save), + int(new_record.ilog_save), new_record.market, new_record.bt_from.isoformat() if new_record.bt_from is not None else None, new_record.bt_to.isoformat() if new_record.bt_to is not None else None, ",".join(str(x) for x in new_record.weekdays_filter) if new_record.weekdays_filter else None, diff --git a/v2realbot/enums/enums.py b/v2realbot/enums/enums.py index bdeb882..cd05a80 100644 --- a/v2realbot/enums/enums.py +++ b/v2realbot/enums/enums.py @@ -103,4 +103,10 @@ class StartBarAlign(str, Enum): RANDOM = first bar starts when first trade occurs """ ROUND = "round" - RANDOM = "random" \ No newline at end of file + RANDOM = "random" + +class Market(str, Enum): + US = "US" + CRYPTO = "CRYPTO" + + \ No newline at end of file diff --git a/v2realbot/scheduler/ap_scheduler.py b/v2realbot/scheduler/ap_scheduler.py index 3a1d4f5..42627cc 100644 --- a/v2realbot/scheduler/ap_scheduler.py +++ b/v2realbot/scheduler/ap_scheduler.py @@ -2,7 +2,7 @@ from uuid import UUID from typing import Any, List, Tuple from uuid import UUID, uuid4 from v2realbot.enums.enums import Moddus, SchedulerStatus, RecordType, StartBarAlign, Mode, Account, OrderSide -from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest +from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest, Market from v2realbot.utils.utils import validate_and_format_time, AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType from datetime import datetime @@ -116,7 +116,8 @@ def initialize_jobs(run_manager_records: RunManagerRecord = None): scheduler.add_job(start_runman_record, start_trigger, id=f"scheduler_start_{record.id}", args=[record.id]) scheduler.add_job(stop_runman_record, stop_trigger, id=f"scheduler_stop_{record.id}", args=[record.id]) - #scheduler.add_job(print_hello, 'interval', seconds=10, id=f"scheduler_testinterval") + #scheduler.add_job(print_hello, 'interval', seconds=10, id= + # f"scheduler_testinterval") scheduled_jobs = scheduler.get_jobs() print(f"APS jobs refreshed ({len(scheduled_jobs)})") current_jobs_dict = format_apscheduler_jobs(scheduled_jobs) @@ -124,9 +125,9 @@ def initialize_jobs(run_manager_records: RunManagerRecord = None): return 0, current_jobs_dict #zastresovaci funkce resici error handling a printing -def start_runman_record(id: UUID, market = "US", debug_date = None): +def start_runman_record(id: UUID, debug_date = None): record = None - res, record, msg = _start_runman_record(id=id, market=market, debug_date=debug_date) + res, record, msg = _start_runman_record(id=id, debug_date=debug_date) if record is not None: market_time_now = datetime.now().astimezone(zoneNY) if debug_date is None else debug_date @@ -165,8 +166,8 @@ def update_runman_record(record: RunManagerRecord): err_msg= f"STOP: Error updating {record.id} errir {set} with values {record}" return -2, err_msg#toto stopne zpracovani dalsich zaznamu pri chybe, zvazit continue -def stop_runman_record(id: UUID, market = "US", debug_date = None): - res, record, msg = _stop_runman_record(id=id, market=market, debug_date=debug_date) +def stop_runman_record(id: UUID, debug_date = None): + res, record, msg = _stop_runman_record(id=id, debug_date=debug_date) #results : 0 - ok, -1 not running/already running/not specific, -2 error #report vzdy zapiseme do history, pokud je record not None, pripadna chyba se stala po dotazeni recordu @@ -196,7 +197,7 @@ def stop_runman_record(id: UUID, market = "US", debug_date = None): print(f"STOP JOB: {id} FINISHED") #start function that is called from the job -def _start_runman_record(id: UUID, market = "US", debug_date = None): +def _start_runman_record(id: UUID, debug_date = None): print(f"Start scheduled record {id}") record : RunManagerRecord = None @@ -207,15 +208,16 @@ def _start_runman_record(id: UUID, market = "US", debug_date = None): record = result - if market is not None and market == "US": - res, sada = sch.get_todays_market_times(market=market, debug_date=debug_date) + if record.market == Market.US or record.market == Market.CRYPTO: + res, sada = sch.get_todays_market_times(market=record.market, debug_date=debug_date) if res == 0: market_time_now, market_open_datetime, market_close_datetime = sada print(f"OPEN:{market_open_datetime} CLOSE:{market_close_datetime}") else: - sada = f"Market {market} Error getting market times (CLOSED): " + str(sada) + sada = f"Market {record.market} Error getting market times (CLOSED): " + str(sada) return res, record, sada - + else: + print("Market type is unknown.") if cs.is_stratin_running(record.strat_id): return -1, record, f"Stratin {record.strat_id} is already running" @@ -229,7 +231,7 @@ def _start_runman_record(id: UUID, market = "US", debug_date = None): return 0, record, record.runner_id #stop function that is called from the job -def _stop_runman_record(id: UUID, market = "US", debug_date = None): +def _stop_runman_record(id: UUID, debug_date = None): record = None #get all records print(f"Stopping record {id}") @@ -304,5 +306,5 @@ if __name__ == "__main__": # print(f"CALL FINISHED, with {debug_date} RESULT: {res}, {result}") - res, result = stop_runman_record(id=id, market = "US", debug_date = debug_date) + res, result = stop_runman_record(id=id, debug_date = debug_date) print(f"CALL FINISHED, with {debug_date} RESULT: {res}, {result}") \ No newline at end of file diff --git a/v2realbot/scheduler/scheduler.py b/v2realbot/scheduler/scheduler.py index bfadc34..9146385 100644 --- a/v2realbot/scheduler/scheduler.py +++ b/v2realbot/scheduler/scheduler.py @@ -2,10 +2,10 @@ import json import datetime import v2realbot.controller.services as cs import v2realbot.controller.run_manager as rm -from v2realbot.common.model import RunnerView, RunManagerRecord, StrategyInstance, Runner, RunRequest, Trade, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest, AnalyzerInputs +from v2realbot.common.model import RunnerView, RunManagerRecord, StrategyInstance, Runner, RunRequest, Trade, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest, AnalyzerInputs, Market from uuid import uuid4, UUID -from v2realbot.utils.utils import json_serial, send_to_telegram, zoneNY, zonePRG, fetch_calendar_data -from datetime import datetime, timedelta +from v2realbot.utils.utils import json_serial, send_to_telegram, zoneNY, zonePRG, zoneUTC, fetch_calendar_data +from datetime import datetime, timedelta, time from traceback import format_exc from rich import print import requests @@ -18,9 +18,18 @@ from v2realbot.config import WEB_API_KEY #naplanovany jako samostatni job a triggerován pouze jednou v daný čas pro start a stop #novy kod v aps_scheduler.py -def get_todays_market_times(market = "US", debug_date = None): +def is_US_market_day(date): + cal_dates = fetch_calendar_data(date, date) + if len(cal_dates) == 0: + print("Today is not a market day.") + return False, cal_dates + else: + print("Market is open") + return True, cal_dates + +def get_todays_market_times(market, debug_date = None): try: - if market == "US": + if market == Market.US: #zjistit vsechny podminky - mozna loopovat - podminky jsou vlevo if debug_date is not None: nowNY = debug_date @@ -28,17 +37,20 @@ def get_todays_market_times(market = "US", debug_date = None): nowNY = datetime.now().astimezone(zoneNY) nowNY_date = nowNY.date() #is market open - nyni pouze US - cal_dates = fetch_calendar_data(nowNY_date, nowNY_date) - - if len(cal_dates) == 0: - print("No Market Day today") - return -1, "Market Closed" + stat, calendar_dates = is_US_market_day(nowNY_date) + if stat: #zatim podpora pouze main session - #pouze main session - market_open_datetime = zoneNY.localize(cal_dates[0].open) - market_close_datetime = zoneNY.localize(cal_dates[0].close) - return 0, (nowNY, market_open_datetime, market_close_datetime) + market_open_datetime = zoneNY.localize(calendar_dates[0].open) + market_close_datetime = zoneNY.localize(calendar_dates[0].close) + return 0, (nowNY, market_open_datetime, market_close_datetime) + else: + return -1, "Market is closed." + elif market == Market.CRYPTO: + now_market_datetime = datetime.now().astimezone(zoneUTC) + market_open_datetime = datetime.combine(datetime.now(), time.min) + matket_close_datetime = datetime.combine(datetime.now(), time.max) + return 0, (now_market_datetime, market_open_datetime, matket_close_datetime) else: return -1, "Market not supported" except Exception as e: diff --git a/v2trading_create_db.sql b/v2trading_create_db.sql new file mode 100644 index 0000000..b17802f --- /dev/null +++ b/v2trading_create_db.sql @@ -0,0 +1,97 @@ +BEGIN TRANSACTION; +CREATE TABLE IF NOT EXISTS "test_list" ( + "id" varchar(32) NOT NULL, + "name" varchar(255) NOT NULL, + "dates" json NOT NULL +); +CREATE TABLE IF NOT EXISTS "runner_detail" ( + "runner_id" varchar(32) NOT NULL, + "data" json NOT NULL, + PRIMARY KEY("runner_id") +); +CREATE TABLE IF NOT EXISTS "runner_header" ( + "runner_id" varchar(32) NOT NULL, + "strat_id" TEXT, + "batch_id" TEXT, + "symbol" TEXT, + "name" TEXT, + "note" TEXT, + "started" TEXT, + "stopped" TEXT, + "mode" TEXT, + "account" TEXT, + "bt_from" TEXT, + "bt_to" TEXT, + "strat_json" TEXT, + "settings" TEXT, + "ilog_save" INTEGER, + "profit" NUMERIC, + "trade_count" INTEGER, + "end_positions" INTEGER, + "end_positions_avgp" NUMERIC, + "metrics" TEXT, + "stratvars_toml" TEXT, + "transferables" TEXT, + PRIMARY KEY("runner_id") +); +CREATE TABLE IF NOT EXISTS "config_table" ( + "id" INTEGER, + "item_name" TEXT NOT NULL, + "json_data" JSON NOT NULL, + "item_lang" TEXT, + PRIMARY KEY("id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "runner_logs" ( + "runner_id" varchar(32) NOT NULL, + "time" real NOT NULL, + "data" json NOT NULL +); +CREATE TABLE "run_manager" ( + "moddus" TEXT NOT NULL, + "id" varchar(32), + "strat_id" varchar(32) NOT NULL, + "symbol" TEXT, + "account" TEXT NOT NULL, + "mode" TEXT NOT NULL, + "note" TEXT, + "ilog_save" BOOLEAN, + "bt_from" TEXT, + "bt_to" TEXT, + "weekdays_filter" TEXT, + "batch_id" TEXT, + "start_time" TEXT NOT NULL, + "stop_time" TEXT NOT NULL, + "status" TEXT NOT NULL, + "last_processed" TEXT, + "history" TEXT, + "valid_from" TEXT, + "valid_to" TEXT, + "testlist_id" TEXT, + "runner_id" varchar2(32), + "market" TEXT, + PRIMARY KEY("id") +); +CREATE INDEX idx_moddus ON run_manager (moddus); +CREATE INDEX idx_status ON run_manager (status); +CREATE INDEX idx_status_moddus ON run_manager (status, moddus); +CREATE INDEX idx_valid_from_to ON run_manager (valid_from, valid_to); +CREATE INDEX idx_stopped_batch_id ON runner_header (stopped, batch_id); +CREATE INDEX idx_search_value ON runner_header (strat_id, batch_id); +CREATE INDEX IF NOT EXISTS "index_runner_header_pk" ON "runner_header" ( + "runner_id" +); +CREATE INDEX IF NOT EXISTS "index_runner_header_strat" ON "runner_header" ( + "strat_id" +); +CREATE INDEX IF NOT EXISTS "index_runner_header_batch" ON "runner_header" ( + "batch_id" +); +CREATE UNIQUE INDEX IF NOT EXISTS "index_runner_detail_pk" ON "runner_detail" ( + "runner_id" +); +CREATE INDEX IF NOT EXISTS "index_runner_logs" ON "runner_logs" ( + "runner_id", + "time" +); +INSERT INTO config_table VALUES (1, "test", "{}", "json"); +COMMIT;