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.
|
||||
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:
|
||||
"""
|
||||
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):
|
||||
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 r_data := self.received_new_trade(msg_data):
|
||||
return standard_reply("trade_created", r_data)
|
||||
|
|
|
|||
|
|
@ -727,11 +727,12 @@ class DatabaseInteractions(SnapshotDataCache):
|
|||
self.db = Database()
|
||||
|
||||
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.
|
||||
|
||||
: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 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.
|
||||
|
|
@ -753,8 +754,11 @@ class DatabaseInteractions(SnapshotDataCache):
|
|||
# Fallback: Fetch from the database and cache the result if necessary
|
||||
result = self._fetch_from_database(cache_name, filter_vals)
|
||||
|
||||
# Take the key out on return.
|
||||
return result.drop(columns=['tbl_key'], errors='ignore')
|
||||
# Remove 'tbl_key' unless include_tbl_key is True
|
||||
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:
|
||||
"""
|
||||
|
|
@ -822,7 +826,12 @@ class DatabaseInteractions(SnapshotDataCache):
|
|||
columns, values = columns + ('tbl_key',), values + (key,)
|
||||
|
||||
# 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
|
||||
if skip_cache:
|
||||
return
|
||||
|
|
@ -869,18 +878,18 @@ class DatabaseInteractions(SnapshotDataCache):
|
|||
# Execute the SQL query to remove the row from the database
|
||||
self.db.execute_sql(sql, params)
|
||||
|
||||
def modify_datacache_item(self, cache_name: str, filter_vals: List[Tuple[str, any]], field_name: str,
|
||||
new_data: any, key: str = None, overwrite: str = None) -> None:
|
||||
def modify_datacache_item(self, cache_name: str, filter_vals: List[Tuple[str, any]], field_names: Tuple[str, ...],
|
||||
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 key: optional key
|
||||
:param cache_name: The name used to identify the cache (also the name of the database table).
|
||||
:param cache_name: The name used to identify the cache.
|
||||
:param filter_vals: A list of tuples containing column names and values to filter by.
|
||||
:param field_name: The field to be updated.
|
||||
:param new_data: The new data to be set.
|
||||
:raises ValueError: If the row is not found in the cache or the database, or if multiple rows are returned.
|
||||
:param field_names: A tuple of field names to be updated.
|
||||
:param new_values: A tuple of new values corresponding to field_names.
|
||||
: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:
|
||||
filter_vals.insert(0, ('tbl_key', key))
|
||||
|
|
@ -893,44 +902,30 @@ class DatabaseInteractions(SnapshotDataCache):
|
|||
|
||||
# Check if multiple rows are returned
|
||||
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'
|
||||
if isinstance(new_data, bool):
|
||||
# If storing in a system that supports booleans, you can store directly as boolean
|
||||
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
|
||||
# Update the DataFrame with the new values
|
||||
for field_name, new_value in zip(field_names, new_values):
|
||||
rows[field_name] = new_value
|
||||
|
||||
# Get the cache instance
|
||||
cache = self.get_cache(cache_name)
|
||||
|
||||
# Set the updated row in the cache
|
||||
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
|
||||
cache.add_entry(key=key_value, data=rows)
|
||||
cache.add_entry(key=key, data=rows)
|
||||
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)
|
||||
else:
|
||||
raise ValueError(f"Unsupported cache type for {cache_name}")
|
||||
|
||||
# Update the value in the database as well
|
||||
sql_update = f"UPDATE {cache_name} SET {field_name} = ? " \
|
||||
f"WHERE {' AND '.join([f'{col} = ?' for col, _ in filter_vals])}"
|
||||
params = [updated_value] + [val for _, val in filter_vals]
|
||||
# Update the values in the database
|
||||
set_clause = ", ".join([f"{field} = ?" for field in field_names])
|
||||
where_clause = " AND ".join([f"{col} = ?" for col, _ 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
|
||||
self.db.execute_sql(sql_update, params)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
import uuid
|
||||
|
||||
import pandas as pd
|
||||
|
||||
|
|
@ -199,18 +200,25 @@ class Strategies:
|
|||
:return: A dictionary containing success or failure information.
|
||||
"""
|
||||
try:
|
||||
# # Create a new Strategy object and add it to the list
|
||||
# self.strat_list.append(Strategy(**data))
|
||||
# Check if a strategy with the same name already exists for this user
|
||||
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
|
||||
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
|
||||
|
||||
# generate a unique identifier
|
||||
tbl_key = str(uuid.uuid4())
|
||||
|
||||
# Insert the strategy into the database and cache
|
||||
self.data.insert_row_into_datacache(
|
||||
cache_name='strategies',
|
||||
columns=("creator", "name", "workspace", "code", "stats", "public", "fee"),
|
||||
columns=("creator", "name", "workspace", "code", "stats", "public", "fee", 'tbl_key'),
|
||||
values=(
|
||||
data.get('creator'),
|
||||
data['name'],
|
||||
|
|
@ -218,7 +226,8 @@ class Strategies:
|
|||
data['code'],
|
||||
stats_serialized, # Serialized stats
|
||||
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)]
|
||||
)
|
||||
|
||||
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:
|
||||
"""
|
||||
Return a list of all strategy names stored in the cache or database.
|
||||
|
|
|
|||
|
|
@ -108,9 +108,10 @@ class BaseUser:
|
|||
self.data.modify_datacache_item(
|
||||
cache_name='users',
|
||||
filter_vals=[('user_name', username)],
|
||||
field_name=field_name,
|
||||
new_data=new_data,
|
||||
overwrite='user_name')
|
||||
field_names=(field_name,),
|
||||
new_values=(new_data,),
|
||||
overwrite='user_name'
|
||||
)
|
||||
|
||||
|
||||
class UserAccountManagement(BaseUser):
|
||||
|
|
|
|||
|
|
@ -361,12 +361,22 @@ class Indicators:
|
|||
return
|
||||
|
||||
# Set visibility for all indicators off
|
||||
self.cache_manager.modify_datacache_item('indicators', [('creator', str(user_id))],
|
||||
field_name='visible', new_data=False, overwrite='name')
|
||||
self.cache_manager.modify_datacache_item(
|
||||
cache_name='indicators',
|
||||
filter_vals=[('creator', str(user_id))],
|
||||
field_names=('visible',),
|
||||
new_values=(False,),
|
||||
overwrite='name'
|
||||
)
|
||||
|
||||
# Set visibility for the specified indicators on
|
||||
self.cache_manager.modify_datacache_item('indicators', [('creator', str(user_id)), ('name', indicator_names)],
|
||||
field_name='visible', new_data=True, overwrite='name')
|
||||
self.cache_manager.modify_datacache_item(
|
||||
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):
|
||||
"""
|
||||
|
|
@ -397,8 +407,8 @@ class Indicators:
|
|||
self.cache_manager.modify_datacache_item(
|
||||
'indicators',
|
||||
[('creator', str(user_id)), ('name', indicator_name)],
|
||||
field_name='properties',
|
||||
new_data=new_properties,
|
||||
field_name=('properties',),
|
||||
new_data=(new_properties,),
|
||||
overwrite='name'
|
||||
)
|
||||
|
||||
|
|
@ -414,8 +424,8 @@ class Indicators:
|
|||
self.cache_manager.modify_datacache_item(
|
||||
'indicators',
|
||||
[('creator', str(user_id)), ('name', indicator_name)],
|
||||
field_name='source',
|
||||
new_data=new_source,
|
||||
field_name=('source',),
|
||||
new_data=(new_source,),
|
||||
overwrite='name'
|
||||
)
|
||||
|
||||
|
|
@ -427,8 +437,8 @@ class Indicators:
|
|||
self.cache_manager.modify_datacache_item(
|
||||
'indicators',
|
||||
[('creator', str(user_id)), ('name', indicator_name)],
|
||||
field_name='visible',
|
||||
new_data=new_visible,
|
||||
field_name=('visible',),
|
||||
new_data=(new_visible,),
|
||||
overwrite='name'
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -228,10 +228,16 @@ class Strategies {
|
|||
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);
|
||||
|
||||
// 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() {
|
||||
this.target = document.getElementById(this.target_id);
|
||||
|
|
|
|||
Loading…
Reference in New Issue