Strategies is now fixed and I am ready to implement backtesting.
This commit is contained in:
parent
adedaaa540
commit
86843e8cb4
|
|
@ -346,6 +346,59 @@ class BrighterTrades:
|
||||||
# Save the new strategy (in both cache and database) and return the result.
|
# Save the new strategy (in both cache and database) and return the result.
|
||||||
return self.strategies.new_strategy(strategy_data)
|
return self.strategies.new_strategy(strategy_data)
|
||||||
|
|
||||||
|
def received_edit_strategy(self, data: dict) -> dict:
|
||||||
|
"""
|
||||||
|
Handles editing an existing strategy based on the provided data.
|
||||||
|
|
||||||
|
:param data: A dictionary containing the attributes of the strategy to edit.
|
||||||
|
:return: A dictionary containing success or failure information.
|
||||||
|
"""
|
||||||
|
# Extract user_name and strategy name from the data
|
||||||
|
user_name = data.get('user_name')
|
||||||
|
strategy_name = data.get('name')
|
||||||
|
if not user_name:
|
||||||
|
return {"success": False, "message": "User not specified"}
|
||||||
|
if not strategy_name:
|
||||||
|
return {"success": False, "message": "Strategy name not specified"}
|
||||||
|
|
||||||
|
# Fetch the user_id using the user_name
|
||||||
|
user_id = self.get_user_info(user_name=user_name, info='User_id')
|
||||||
|
if not user_id:
|
||||||
|
return {"success": False, "message": "User ID not found"}
|
||||||
|
|
||||||
|
# Retrieve the tbl_key using user_id and strategy name
|
||||||
|
filter_conditions = [('creator', user_id), ('name', strategy_name)]
|
||||||
|
strategy_row = self.data.get_rows_from_datacache(
|
||||||
|
cache_name='strategies',
|
||||||
|
filter_vals=filter_conditions,
|
||||||
|
include_tbl_key=True # Include tbl_key in the result
|
||||||
|
)
|
||||||
|
|
||||||
|
if strategy_row.empty:
|
||||||
|
return {"success": False, "message": "Strategy not found"}
|
||||||
|
|
||||||
|
# Ensure only one strategy is found
|
||||||
|
if len(strategy_row) > 1:
|
||||||
|
return {"success": False, "message": "Multiple strategies found. Please provide more specific information."}
|
||||||
|
|
||||||
|
# Extract the tbl_key
|
||||||
|
tbl_key = strategy_row.iloc[0]['tbl_key']
|
||||||
|
|
||||||
|
# Prepare the updated strategy data
|
||||||
|
strategy_data = {
|
||||||
|
"creator": user_id,
|
||||||
|
"name": strategy_name,
|
||||||
|
"workspace": data.get('workspace'),
|
||||||
|
"code": data.get('code'),
|
||||||
|
"stats": data.get('stats', {}),
|
||||||
|
"public": data.get('public', 0),
|
||||||
|
"fee": data.get('fee', None),
|
||||||
|
"tbl_key": tbl_key # Include the tbl_key to identify the strategy
|
||||||
|
}
|
||||||
|
|
||||||
|
# Call the edit_strategy method to update the strategy
|
||||||
|
return self.strategies.edit_strategy(strategy_data)
|
||||||
|
|
||||||
def delete_strategy(self, data: dict) -> str | dict:
|
def delete_strategy(self, data: dict) -> str | dict:
|
||||||
"""
|
"""
|
||||||
Deletes the specified strategy from the strategies instance and the configuration file.
|
Deletes the specified strategy from the strategies instance and the configuration file.
|
||||||
|
|
@ -640,6 +693,10 @@ class BrighterTrades:
|
||||||
if r_data := self.received_new_strategy(msg_data):
|
if r_data := self.received_new_strategy(msg_data):
|
||||||
return standard_reply("strategy_created", r_data)
|
return standard_reply("strategy_created", r_data)
|
||||||
|
|
||||||
|
if msg_type == 'edit_strategy':
|
||||||
|
if r_data := self.received_edit_strategy(msg_data):
|
||||||
|
return standard_reply("strategy_created", r_data)
|
||||||
|
|
||||||
if msg_type == 'new_trade':
|
if msg_type == 'new_trade':
|
||||||
if r_data := self.received_new_trade(msg_data):
|
if r_data := self.received_new_trade(msg_data):
|
||||||
return standard_reply("trade_created", r_data)
|
return standard_reply("trade_created", r_data)
|
||||||
|
|
|
||||||
|
|
@ -727,11 +727,12 @@ class DatabaseInteractions(SnapshotDataCache):
|
||||||
self.db = Database()
|
self.db = Database()
|
||||||
|
|
||||||
def get_rows_from_datacache(self, cache_name: str, filter_vals: list[tuple[str, Any]] = None,
|
def get_rows_from_datacache(self, cache_name: str, filter_vals: list[tuple[str, Any]] = None,
|
||||||
key: str = None) -> pd.DataFrame | None:
|
key: str = None, include_tbl_key: bool = False) -> pd.DataFrame | None:
|
||||||
"""
|
"""
|
||||||
Retrieves rows from the cache if available; otherwise, queries the database and caches the result.
|
Retrieves rows from the cache if available; otherwise, queries the database and caches the result.
|
||||||
|
|
||||||
:param key: Optional
|
:param include_tbl_key: If True, includes 'tbl_key' in the returned DataFrame.
|
||||||
|
:param key: Optional key to filter by 'tbl_key'.
|
||||||
:param cache_name: The key used to identify the cache (also the name of the database table).
|
:param cache_name: The key used to identify the cache (also the name of the database table).
|
||||||
:param filter_vals: A list of tuples, each containing a column name and the value(s) to filter by.
|
:param filter_vals: A list of tuples, each containing a column name and the value(s) to filter by.
|
||||||
:return: A DataFrame containing the requested rows, or None if no matching rows are found.
|
:return: A DataFrame containing the requested rows, or None if no matching rows are found.
|
||||||
|
|
@ -753,8 +754,11 @@ class DatabaseInteractions(SnapshotDataCache):
|
||||||
# Fallback: Fetch from the database and cache the result if necessary
|
# Fallback: Fetch from the database and cache the result if necessary
|
||||||
result = self._fetch_from_database(cache_name, filter_vals)
|
result = self._fetch_from_database(cache_name, filter_vals)
|
||||||
|
|
||||||
# Take the key out on return.
|
# Remove 'tbl_key' unless include_tbl_key is True
|
||||||
return result.drop(columns=['tbl_key'], errors='ignore')
|
if not include_tbl_key:
|
||||||
|
result = result.drop(columns=['tbl_key'], errors='ignore')
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def _fetch_from_database(self, cache_name: str, filter_vals: List[tuple[str, Any]]) -> pd.DataFrame:
|
def _fetch_from_database(self, cache_name: str, filter_vals: List[tuple[str, Any]]) -> pd.DataFrame:
|
||||||
"""
|
"""
|
||||||
|
|
@ -822,7 +826,12 @@ class DatabaseInteractions(SnapshotDataCache):
|
||||||
columns, values = columns + ('tbl_key',), values + (key,)
|
columns, values = columns + ('tbl_key',), values + (key,)
|
||||||
|
|
||||||
# Insert the row into the database
|
# Insert the row into the database
|
||||||
self.db.insert_row(table=cache_name, columns=columns, values=values)
|
last_inserted_id = self.db.insert_row(table=cache_name, columns=columns, values=values)
|
||||||
|
|
||||||
|
# Now include the 'id' in the columns and values when inserting into the cache
|
||||||
|
columns = ('id',) + columns
|
||||||
|
values = (last_inserted_id,) + values
|
||||||
|
|
||||||
# Insert the row into the cache
|
# Insert the row into the cache
|
||||||
if skip_cache:
|
if skip_cache:
|
||||||
return
|
return
|
||||||
|
|
@ -869,18 +878,18 @@ class DatabaseInteractions(SnapshotDataCache):
|
||||||
# Execute the SQL query to remove the row from the database
|
# Execute the SQL query to remove the row from the database
|
||||||
self.db.execute_sql(sql, params)
|
self.db.execute_sql(sql, params)
|
||||||
|
|
||||||
def modify_datacache_item(self, cache_name: str, filter_vals: List[Tuple[str, any]], field_name: str,
|
def modify_datacache_item(self, cache_name: str, filter_vals: List[Tuple[str, any]], field_names: Tuple[str, ...],
|
||||||
new_data: any, key: str = None, overwrite: str = None) -> None:
|
new_values: Tuple[Any, ...], key: str = None, overwrite: str = None) -> None:
|
||||||
"""
|
"""
|
||||||
Modifies a specific field in a row within the cache and updates the database accordingly.
|
Modifies specific fields in a row within the cache and updates the database accordingly.
|
||||||
|
|
||||||
:param overwrite:
|
:param cache_name: The name used to identify the cache.
|
||||||
:param key: optional key
|
|
||||||
:param cache_name: The name used to identify the cache (also the name of the database table).
|
|
||||||
:param filter_vals: A list of tuples containing column names and values to filter by.
|
:param filter_vals: A list of tuples containing column names and values to filter by.
|
||||||
:param field_name: The field to be updated.
|
:param field_names: A tuple of field names to be updated.
|
||||||
:param new_data: The new data to be set.
|
:param new_values: A tuple of new values corresponding to field_names.
|
||||||
:raises ValueError: If the row is not found in the cache or the database, or if multiple rows are returned.
|
:param key: Optional key to identify the entry.
|
||||||
|
:param overwrite: Column name(s) to use for overwriting in the cache.
|
||||||
|
:raises ValueError: If the row is not found or multiple rows are returned.
|
||||||
"""
|
"""
|
||||||
if key:
|
if key:
|
||||||
filter_vals.insert(0, ('tbl_key', key))
|
filter_vals.insert(0, ('tbl_key', key))
|
||||||
|
|
@ -893,44 +902,30 @@ class DatabaseInteractions(SnapshotDataCache):
|
||||||
|
|
||||||
# Check if multiple rows are returned
|
# Check if multiple rows are returned
|
||||||
if len(rows) > 1:
|
if len(rows) > 1:
|
||||||
raise ValueError(f"Multiple rows found for {filter_vals}. Please provide a more specific filter.")
|
raise ValueError(f"Multiple rows found for {filter_vals}. Please provide more specific filter.")
|
||||||
|
|
||||||
# Ensure consistency when storing boolean data like 'visible'
|
# Update the DataFrame with the new values
|
||||||
if isinstance(new_data, bool):
|
for field_name, new_value in zip(field_names, new_values):
|
||||||
# If storing in a system that supports booleans, you can store directly as boolean
|
rows[field_name] = new_value
|
||||||
updated_value = new_data # For cache systems that support booleans
|
|
||||||
|
|
||||||
# If your cache or database only supports strings, convert boolean to string
|
|
||||||
# updated_value = 'true' if new_data else 'false'
|
|
||||||
elif isinstance(new_data, str):
|
|
||||||
updated_value = new_data
|
|
||||||
else:
|
|
||||||
updated_value = json.dumps(new_data) # Convert non-string data to JSON string if necessary
|
|
||||||
|
|
||||||
# Update the DataFrame with the new value
|
|
||||||
rows[field_name] = updated_value
|
|
||||||
|
|
||||||
# Get the cache instance
|
# Get the cache instance
|
||||||
cache = self.get_cache(cache_name)
|
cache = self.get_cache(cache_name)
|
||||||
|
|
||||||
# Set the updated row in the cache
|
# Set the updated row in the cache
|
||||||
if isinstance(cache, RowBasedCache):
|
if isinstance(cache, RowBasedCache):
|
||||||
# For row-based cache, the 'tbl_key' must be in filter_vals
|
|
||||||
key_value = next((val for key, val in filter_vals if key == 'tbl_key'), None)
|
|
||||||
if key_value is None:
|
|
||||||
raise ValueError("'tbl_key' must be present in filter_vals for row-based caches.")
|
|
||||||
# Update the cache entry with the modified row
|
# Update the cache entry with the modified row
|
||||||
cache.add_entry(key=key_value, data=rows)
|
cache.add_entry(key=key, data=rows)
|
||||||
elif isinstance(cache, TableBasedCache):
|
elif isinstance(cache, TableBasedCache):
|
||||||
# For table-based cache, use the existing query method to update the correct rows
|
# Use 'overwrite' to ensure correct row is updated
|
||||||
cache.add_table(rows, overwrite=overwrite)
|
cache.add_table(rows, overwrite=overwrite)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unsupported cache type for {cache_name}")
|
raise ValueError(f"Unsupported cache type for {cache_name}")
|
||||||
|
|
||||||
# Update the value in the database as well
|
# Update the values in the database
|
||||||
sql_update = f"UPDATE {cache_name} SET {field_name} = ? " \
|
set_clause = ", ".join([f"{field} = ?" for field in field_names])
|
||||||
f"WHERE {' AND '.join([f'{col} = ?' for col, _ in filter_vals])}"
|
where_clause = " AND ".join([f"{col} = ?" for col, _ in filter_vals])
|
||||||
params = [updated_value] + [val for _, val in filter_vals]
|
sql_update = f"UPDATE {cache_name} SET {set_clause} WHERE {where_clause}"
|
||||||
|
params = list(new_values) + [val for _, val in filter_vals]
|
||||||
|
|
||||||
# Execute the SQL update to modify the database
|
# Execute the SQL update to modify the database
|
||||||
self.db.execute_sql(sql_update, params)
|
self.db.execute_sql(sql_update, params)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import json
|
import json
|
||||||
|
import uuid
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
|
|
@ -199,18 +200,25 @@ class Strategies:
|
||||||
:return: A dictionary containing success or failure information.
|
:return: A dictionary containing success or failure information.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# # Create a new Strategy object and add it to the list
|
# Check if a strategy with the same name already exists for this user
|
||||||
# self.strat_list.append(Strategy(**data))
|
filter_conditions = [('creator', data.get('creator')), ('name', data['name'])]
|
||||||
|
existing_strategy = self.data.get_rows_from_datacache(cache_name='strategies',
|
||||||
|
filter_vals=filter_conditions)
|
||||||
|
if not existing_strategy.empty:
|
||||||
|
return {"success": False, "message": "A strategy with this name already exists"}
|
||||||
|
|
||||||
# Serialize complex data fields like workspace and stats
|
# Serialize complex data fields like workspace and stats
|
||||||
workspace_serialized = json.dumps(data['workspace']) if isinstance(data['workspace'], dict) else data[
|
workspace_serialized = json.dumps(data['workspace']) if isinstance(data['workspace'], dict) else data[
|
||||||
'workspace']
|
'workspace']
|
||||||
stats_serialized = json.dumps(data.get('stats', {})) # Convert stats to a JSON string
|
stats_serialized = json.dumps(data.get('stats', {})) # Convert stats to a JSON string
|
||||||
|
|
||||||
|
# generate a unique identifier
|
||||||
|
tbl_key = str(uuid.uuid4())
|
||||||
|
|
||||||
# Insert the strategy into the database and cache
|
# Insert the strategy into the database and cache
|
||||||
self.data.insert_row_into_datacache(
|
self.data.insert_row_into_datacache(
|
||||||
cache_name='strategies',
|
cache_name='strategies',
|
||||||
columns=("creator", "name", "workspace", "code", "stats", "public", "fee"),
|
columns=("creator", "name", "workspace", "code", "stats", "public", "fee", 'tbl_key'),
|
||||||
values=(
|
values=(
|
||||||
data.get('creator'),
|
data.get('creator'),
|
||||||
data['name'],
|
data['name'],
|
||||||
|
|
@ -218,7 +226,8 @@ class Strategies:
|
||||||
data['code'],
|
data['code'],
|
||||||
stats_serialized, # Serialized stats
|
stats_serialized, # Serialized stats
|
||||||
data.get('public', False),
|
data.get('public', False),
|
||||||
data.get('fee', 0)
|
data.get('fee', 0),
|
||||||
|
tbl_key
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -243,6 +252,51 @@ class Strategies:
|
||||||
filter_vals=[('creator', user_id), ('name', name)]
|
filter_vals=[('creator', user_id), ('name', name)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def edit_strategy(self, data: dict) -> dict:
|
||||||
|
"""
|
||||||
|
Updates an existing strategy in the cache and database.
|
||||||
|
|
||||||
|
:param data: A dictionary containing the updated strategy data.
|
||||||
|
:return: A dictionary containing success or failure information.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
tbl_key = data['tbl_key'] # The unique identifier for the strategy
|
||||||
|
|
||||||
|
# Serialize complex data fields like workspace and stats
|
||||||
|
workspace_serialized = json.dumps(data['workspace']) if isinstance(data['workspace'], dict) else data[
|
||||||
|
'workspace']
|
||||||
|
stats_serialized = json.dumps(data.get('stats', {})) # Convert stats to a JSON string
|
||||||
|
|
||||||
|
# Prepare the columns and values for the update
|
||||||
|
columns = ("creator", "name", "workspace", "code", "stats", "public", "fee", "tbl_key")
|
||||||
|
values = (
|
||||||
|
data.get('creator'),
|
||||||
|
data['name'],
|
||||||
|
workspace_serialized, # Serialized workspace
|
||||||
|
data['code'],
|
||||||
|
stats_serialized, # Serialized stats
|
||||||
|
data.get('public', False),
|
||||||
|
data.get('fee', 0),
|
||||||
|
tbl_key
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update the strategy in the database and cache
|
||||||
|
self.data.modify_datacache_item(
|
||||||
|
cache_name='strategies',
|
||||||
|
filter_vals=[('tbl_key', tbl_key)],
|
||||||
|
field_names=columns,
|
||||||
|
new_values=values,
|
||||||
|
key=tbl_key,
|
||||||
|
overwrite='tbl_key' # Use 'tbl_key' to identify the entry to overwrite
|
||||||
|
)
|
||||||
|
|
||||||
|
# Return success message
|
||||||
|
return {"success": True, "message": "Strategy updated successfully"}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Handle exceptions and return failure message
|
||||||
|
return {"success": False, "message": f"Failed to update strategy: {str(e)}"}
|
||||||
|
|
||||||
def get_all_strategy_names(self, user_id) -> list | None:
|
def get_all_strategy_names(self, user_id) -> list | None:
|
||||||
"""
|
"""
|
||||||
Return a list of all strategy names stored in the cache or database.
|
Return a list of all strategy names stored in the cache or database.
|
||||||
|
|
|
||||||
|
|
@ -108,9 +108,10 @@ class BaseUser:
|
||||||
self.data.modify_datacache_item(
|
self.data.modify_datacache_item(
|
||||||
cache_name='users',
|
cache_name='users',
|
||||||
filter_vals=[('user_name', username)],
|
filter_vals=[('user_name', username)],
|
||||||
field_name=field_name,
|
field_names=(field_name,),
|
||||||
new_data=new_data,
|
new_values=(new_data,),
|
||||||
overwrite='user_name')
|
overwrite='user_name'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserAccountManagement(BaseUser):
|
class UserAccountManagement(BaseUser):
|
||||||
|
|
|
||||||
|
|
@ -361,12 +361,22 @@ class Indicators:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set visibility for all indicators off
|
# Set visibility for all indicators off
|
||||||
self.cache_manager.modify_datacache_item('indicators', [('creator', str(user_id))],
|
self.cache_manager.modify_datacache_item(
|
||||||
field_name='visible', new_data=False, overwrite='name')
|
cache_name='indicators',
|
||||||
|
filter_vals=[('creator', str(user_id))],
|
||||||
|
field_names=('visible',),
|
||||||
|
new_values=(False,),
|
||||||
|
overwrite='name'
|
||||||
|
)
|
||||||
|
|
||||||
# Set visibility for the specified indicators on
|
# Set visibility for the specified indicators on
|
||||||
self.cache_manager.modify_datacache_item('indicators', [('creator', str(user_id)), ('name', indicator_names)],
|
self.cache_manager.modify_datacache_item(
|
||||||
field_name='visible', new_data=True, overwrite='name')
|
cache_name='indicators',
|
||||||
|
filter_vals=[('creator', str(user_id)), ('name', indicator_names)],
|
||||||
|
field_names=('visible',),
|
||||||
|
new_values=(True,),
|
||||||
|
overwrite='name'
|
||||||
|
)
|
||||||
|
|
||||||
def edit_indicator(self, user_name: str, params: dict):
|
def edit_indicator(self, user_name: str, params: dict):
|
||||||
"""
|
"""
|
||||||
|
|
@ -397,8 +407,8 @@ class Indicators:
|
||||||
self.cache_manager.modify_datacache_item(
|
self.cache_manager.modify_datacache_item(
|
||||||
'indicators',
|
'indicators',
|
||||||
[('creator', str(user_id)), ('name', indicator_name)],
|
[('creator', str(user_id)), ('name', indicator_name)],
|
||||||
field_name='properties',
|
field_name=('properties',),
|
||||||
new_data=new_properties,
|
new_data=(new_properties,),
|
||||||
overwrite='name'
|
overwrite='name'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -414,8 +424,8 @@ class Indicators:
|
||||||
self.cache_manager.modify_datacache_item(
|
self.cache_manager.modify_datacache_item(
|
||||||
'indicators',
|
'indicators',
|
||||||
[('creator', str(user_id)), ('name', indicator_name)],
|
[('creator', str(user_id)), ('name', indicator_name)],
|
||||||
field_name='source',
|
field_name=('source',),
|
||||||
new_data=new_source,
|
new_data=(new_source,),
|
||||||
overwrite='name'
|
overwrite='name'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -427,8 +437,8 @@ class Indicators:
|
||||||
self.cache_manager.modify_datacache_item(
|
self.cache_manager.modify_datacache_item(
|
||||||
'indicators',
|
'indicators',
|
||||||
[('creator', str(user_id)), ('name', indicator_name)],
|
[('creator', str(user_id)), ('name', indicator_name)],
|
||||||
field_name='visible',
|
field_name=('visible',),
|
||||||
new_data=new_visible,
|
new_data=(new_visible,),
|
||||||
overwrite='name'
|
overwrite='name'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -228,10 +228,16 @@ class Strategies {
|
||||||
strategy_name: name // Strategy name to be deleted
|
strategy_name: name // Strategy name to be deleted
|
||||||
};
|
};
|
||||||
|
|
||||||
// Corrected call to send message type and data separately
|
|
||||||
window.UI.data.comms.sendToApp('delete_strategy', deleteData);
|
window.UI.data.comms.sendToApp('delete_strategy', deleteData);
|
||||||
|
|
||||||
|
// Remove the strategy from the local array
|
||||||
|
this.strategies = this.strategies.filter(strat => strat.name !== name);
|
||||||
|
|
||||||
|
// Update the UI
|
||||||
|
this.update_html();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Initialize strategies
|
// Initialize strategies
|
||||||
initialize() {
|
initialize() {
|
||||||
this.target = document.getElementById(this.target_id);
|
this.target = document.getElementById(this.target_id);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue