Mostly working now
This commit is contained in:
parent
f1d0f2a4b1
commit
29f30cb358
|
|
@ -895,8 +895,14 @@ class DatabaseInteractions(SnapshotDataCache):
|
||||||
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 a more specific filter.")
|
||||||
|
|
||||||
# Modify the specified field
|
# Ensure consistency when storing boolean data like 'visible'
|
||||||
if isinstance(new_data, str):
|
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
|
updated_value = new_data
|
||||||
else:
|
else:
|
||||||
updated_value = json.dumps(new_data) # Convert non-string data to JSON string if necessary
|
updated_value = json.dumps(new_data) # Convert non-string data to JSON string if necessary
|
||||||
|
|
|
||||||
|
|
@ -309,7 +309,7 @@ class Indicators:
|
||||||
:param only_enabled: bool - If True, return only indicators marked as visible.
|
:param only_enabled: bool - If True, return only indicators marked as visible.
|
||||||
:return: dict - A dictionary of indicator names as keys and their attributes as values.
|
:return: dict - A dictionary of indicator names as keys and their attributes as values.
|
||||||
"""
|
"""
|
||||||
user_id = str(self.users.get_id(username))
|
user_id = self.users.get_id(username)
|
||||||
|
|
||||||
if not user_id:
|
if not user_id:
|
||||||
raise ValueError(f"Invalid user_name: {username}")
|
raise ValueError(f"Invalid user_name: {username}")
|
||||||
|
|
@ -317,7 +317,7 @@ class Indicators:
|
||||||
# Fetch indicators based on visibility status
|
# Fetch indicators based on visibility status
|
||||||
if only_enabled:
|
if only_enabled:
|
||||||
indicators_df = self.cache_manager.get_rows_from_datacache('indicators',
|
indicators_df = self.cache_manager.get_rows_from_datacache('indicators',
|
||||||
[('creator', user_id), ('visible', str(1))])
|
[('creator', user_id), ('visible', True)])
|
||||||
else:
|
else:
|
||||||
indicators_df = self.cache_manager.get_rows_from_datacache('indicators', [('creator', user_id)])
|
indicators_df = self.cache_manager.get_rows_from_datacache('indicators', [('creator', user_id)])
|
||||||
|
|
||||||
|
|
@ -333,10 +333,15 @@ class Indicators:
|
||||||
properties = row.get('properties', {})
|
properties = row.get('properties', {})
|
||||||
properties = json.loads(properties) if isinstance(properties, str) else properties
|
properties = json.loads(properties) if isinstance(properties, str) else properties
|
||||||
|
|
||||||
|
# Deserialize the 'source' field if it is a JSON string
|
||||||
|
source = row.get('source', {})
|
||||||
|
source = json.loads(source) if isinstance(source, str) else source
|
||||||
|
|
||||||
# Construct the result dictionary for each indicator
|
# Construct the result dictionary for each indicator
|
||||||
result[row['name']] = {
|
result[row['name']] = {
|
||||||
'type': row['kind'],
|
'type': row['kind'],
|
||||||
'visible': row['visible'],
|
'visible': bool(row['visible']),
|
||||||
|
'source': source,
|
||||||
**properties # Merge in all properties from the properties field
|
**properties # Merge in all properties from the properties field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -351,17 +356,17 @@ class Indicators:
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
indicators = self.cache_manager.get_rows_from_datacache('indicators', [('creator', user_id)])
|
indicators = self.cache_manager.get_rows_from_datacache('indicators', [('creator', user_id)])
|
||||||
# Validate inputs
|
|
||||||
if indicators.empty:
|
if indicators.empty:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Set visibility for all indicators off
|
# Set visibility for all indicators off
|
||||||
self.cache_manager.modify_datacache_item('indicators', [('creator', user_id)],
|
self.cache_manager.modify_datacache_item('indicators', [('creator', user_id)],
|
||||||
field_name='visible', new_data=0, overwrite='name')
|
field_name='visible', new_data=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', user_id), ('name', indicator_names)],
|
self.cache_manager.modify_datacache_item('indicators', [('creator', user_id), ('name', indicator_names)],
|
||||||
field_name='visible', new_data=1, overwrite='name')
|
field_name='visible', new_data=True, overwrite='name')
|
||||||
|
|
||||||
def edit_indicator(self, user_name: str, params: dict):
|
def edit_indicator(self, user_name: str, params: dict):
|
||||||
"""
|
"""
|
||||||
|
|
@ -374,27 +379,58 @@ class Indicators:
|
||||||
raise ValueError("Indicator name is required for editing.")
|
raise ValueError("Indicator name is required for editing.")
|
||||||
|
|
||||||
# Get the indicator from the user's indicator list
|
# Get the indicator from the user's indicator list
|
||||||
user_id = str(self.users.get_id(user_name))
|
user_id = self.users.get_id(user_name)
|
||||||
indicator = self.cache_manager.get_rows_from_datacache('indicators',
|
indicator = self.cache_manager.get_rows_from_datacache('indicators',
|
||||||
[('name', indicator_name), ('creator', user_id)])
|
[('name', indicator_name), ('creator', user_id)])
|
||||||
|
|
||||||
if indicator.empty:
|
if indicator.empty:
|
||||||
raise ValueError(f"Indicator '{indicator_name}' not found for user '{user_name}'.")
|
raise ValueError(f"Indicator '{indicator_name}' not found for user '{user_name}'.")
|
||||||
|
|
||||||
# Modify indicator.
|
# Compare existing_properties and new_properties as strings
|
||||||
self.cache_manager.modify_datacache_item('indicators',
|
new_properties = params.get('properties')
|
||||||
[('creator', user_id), ('name', indicator_name)],
|
if new_properties is not None:
|
||||||
field_name='properties', new_data=params.get('properties'),
|
new_properties_str = json.dumps(new_properties, sort_keys=True) # Convert new properties to a JSON string
|
||||||
overwrite='name')
|
existing_properties_str = indicator['properties'].iloc[0] # Existing properties are already a JSON string
|
||||||
|
|
||||||
new_visible = params.get('visible')
|
# Compare the strings directly
|
||||||
current_visible = indicator['visible'].iloc[0]
|
if existing_properties_str != new_properties_str:
|
||||||
|
self.cache_manager.modify_datacache_item(
|
||||||
|
'indicators',
|
||||||
|
[('creator', user_id), ('name', indicator_name)],
|
||||||
|
field_name='properties',
|
||||||
|
new_data=new_properties,
|
||||||
|
overwrite='name'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Compare existing_source and new_source as strings
|
||||||
|
new_source = params.get('source')
|
||||||
|
if new_source is not None:
|
||||||
|
new_source_str = json.dumps(new_source, sort_keys=True) # Convert new source to a JSON string
|
||||||
|
existing_source_str = indicator['source'].iloc[
|
||||||
|
0] if 'source' in indicator else None # Existing source as JSON string
|
||||||
|
|
||||||
|
# Compare the strings directly
|
||||||
|
if existing_source_str != new_source_str and new_source_str is not None:
|
||||||
|
self.cache_manager.modify_datacache_item(
|
||||||
|
'indicators',
|
||||||
|
[('creator', user_id), ('name', indicator_name)],
|
||||||
|
field_name='source',
|
||||||
|
new_data=new_source,
|
||||||
|
overwrite='name'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Convert current_visible to boolean and check if it has changed
|
||||||
|
current_visible = str(indicator['visible'].iloc[0]).lower() in ['true', '1', 't', 'yes']
|
||||||
|
new_visible = bool(params.get('visible'))
|
||||||
|
|
||||||
if current_visible != new_visible:
|
if current_visible != new_visible:
|
||||||
self.cache_manager.modify_datacache_item('indicators',
|
self.cache_manager.modify_datacache_item(
|
||||||
|
'indicators',
|
||||||
[('creator', user_id), ('name', indicator_name)],
|
[('creator', user_id), ('name', indicator_name)],
|
||||||
field_name='visible', new_data=new_visible,
|
field_name='visible',
|
||||||
overwrite='name')
|
new_data=new_visible,
|
||||||
|
overwrite='name'
|
||||||
|
)
|
||||||
|
|
||||||
def new_indicator(self, user_name: str, params) -> None:
|
def new_indicator(self, user_name: str, params) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
@ -404,26 +440,22 @@ class Indicators:
|
||||||
:param params: The request parameters containing indicator information.
|
:param params: The request parameters containing indicator information.
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
indcr = params['newi_name']
|
indcr = params['name']
|
||||||
indtyp = params['newi_type']
|
indtyp = params['type']
|
||||||
|
|
||||||
# Validate indicator name and type
|
# Validate indicator name and type
|
||||||
if not indcr:
|
if not indcr:
|
||||||
raise ValueError("Indicator name is required.")
|
raise ValueError("Indicator name is required.")
|
||||||
if indtyp not in ['SMA', 'EMA', 'RSI', 'LREG', 'ATR', 'BOLBands', 'MACD', 'Volume']:
|
if indtyp not in indicators_registry:
|
||||||
raise ValueError("Unsupported indicator type.")
|
raise ValueError("Unsupported indicator type.")
|
||||||
|
|
||||||
# Create a dictionary of properties from the values in request form.
|
# Create a dictionary of properties from the values in request form.
|
||||||
source = {
|
source = params['source']
|
||||||
'symbol': params['ei_symbol'],
|
|
||||||
'timeframe': params['ei_timeframe'],
|
|
||||||
'exchange_name': params['ei_exchange_name']
|
|
||||||
}
|
|
||||||
|
|
||||||
# Validate properties (assuming properties is a dictionary)
|
# Validate properties (assuming properties is a dictionary)
|
||||||
properties = {}
|
properties = {}
|
||||||
if params['new_prop_obj']:
|
if params['properties']:
|
||||||
properties = json.loads(params['new_prop_obj'])
|
properties = json.loads(params['properties'])
|
||||||
|
|
||||||
# Create indicator.
|
# Create indicator.
|
||||||
self.create_indicator(creator=user_name, name=indcr, kind=indtyp, source=source, properties=properties)
|
self.create_indicator(creator=user_name, name=indcr, kind=indtyp, source=source, properties=properties)
|
||||||
|
|
@ -438,7 +470,7 @@ class Indicators:
|
||||||
"""
|
"""
|
||||||
username = self.users.get_username(indicator.creator)
|
username = self.users.get_username(indicator.creator)
|
||||||
src = indicator.source
|
src = indicator.source
|
||||||
symbol, timeframe, exchange_name = src['symbol'], src['timeframe'], src['exchange_name']
|
symbol, timeframe, exchange_name = src['market'], src['timeframe'], src['exchange']
|
||||||
|
|
||||||
# Retrieve necessary details to instantiate the indicator
|
# Retrieve necessary details to instantiate the indicator
|
||||||
name = indicator.name
|
name = indicator.name
|
||||||
|
|
@ -502,11 +534,11 @@ class Indicators:
|
||||||
|
|
||||||
# Extract fields from indicators['source'] and compare directly
|
# Extract fields from indicators['source'] and compare directly
|
||||||
mask = (indicators['source'].apply(lambda s: s.get('timeframe')) == source_timeframe) & \
|
mask = (indicators['source'].apply(lambda s: s.get('timeframe')) == source_timeframe) & \
|
||||||
(indicators['source'].apply(lambda s: s.get('exchange_name')) == source_exchange) & \
|
(indicators['source'].apply(lambda s: s.get('exchange')) == source_exchange) & \
|
||||||
(indicators['source'].apply(lambda s: s.get('symbol')) == source_symbol)
|
(indicators['source'].apply(lambda s: s.get('market')) == source_symbol)
|
||||||
|
|
||||||
# Filter the DataFrame using the mask
|
# Filter the DataFrame using the mask
|
||||||
filtered_indicators = indicators[mask]
|
indicators = indicators[mask]
|
||||||
|
|
||||||
# If no indicators match the filtered source, return None.
|
# If no indicators match the filtered source, return None.
|
||||||
if indicators.empty:
|
if indicators.empty:
|
||||||
|
|
@ -515,7 +547,7 @@ class Indicators:
|
||||||
# Process each indicator, convert DataFrame to JSON-serializable format, and collect the results
|
# Process each indicator, convert DataFrame to JSON-serializable format, and collect the results
|
||||||
json_ready_results = {}
|
json_ready_results = {}
|
||||||
|
|
||||||
for indicator in filtered_indicators.itertuples(index=False):
|
for indicator in indicators.itertuples(index=False):
|
||||||
indicator_results = self.process_indicator(indicator=indicator, num_results=num_results)
|
indicator_results = self.process_indicator(indicator=indicator, num_results=num_results)
|
||||||
|
|
||||||
# Convert DataFrame to list of dictionaries if necessary
|
# Convert DataFrame to list of dictionaries if necessary
|
||||||
|
|
@ -569,15 +601,22 @@ class Indicators:
|
||||||
if kind not in self.indicator_registry:
|
if kind not in self.indicator_registry:
|
||||||
raise ValueError(f"Requested an unsupported type of indicator: ({kind})")
|
raise ValueError(f"Requested an unsupported type of indicator: ({kind})")
|
||||||
|
|
||||||
|
# Instantiate the indicator class from the registry
|
||||||
|
IndicatorClass = indicators_registry[kind]
|
||||||
|
indicator_instance = IndicatorClass(name=name, indicator_type=kind, properties=properties)
|
||||||
|
|
||||||
|
# Sanitize source and properties by converting them to JSON strings
|
||||||
|
sanitized_source = json.dumps(source) # Converts the dictionary to a JSON string
|
||||||
|
sanitized_properties = json.dumps(indicator_instance.properties) # Same for properties
|
||||||
|
|
||||||
# Add the new indicator to a pandas dataframe.
|
# Add the new indicator to a pandas dataframe.
|
||||||
creator_id = self.users.get_id(creator)
|
|
||||||
row_data = pd.DataFrame([{
|
row_data = pd.DataFrame([{
|
||||||
'creator': creator_id,
|
'creator': creator_id,
|
||||||
'name': name,
|
'name': name,
|
||||||
'kind': kind,
|
'kind': kind,
|
||||||
'visible': visible,
|
'visible': bool(visible),
|
||||||
'source': source,
|
'source': sanitized_source,
|
||||||
'properties': properties
|
'properties': sanitized_properties
|
||||||
}])
|
}])
|
||||||
self.cache_manager.insert_df_into_datacache(df=row_data, cache_name="indicators", skip_cache=False)
|
self.cache_manager.insert_df_into_datacache(df=row_data, cache_name="indicators", skip_cache=False)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,28 @@ class Comms {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to update an indicator's properties.
|
||||||
|
* @param {Object} indicatorData - An object containing the updated properties of the indicator.
|
||||||
|
* @returns {Promise<Object>} - The response from the server.
|
||||||
|
*/
|
||||||
|
async submitIndicator(indicatorData) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/settings', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
setting: 'new_indicator',
|
||||||
|
indicator: indicatorData
|
||||||
|
})
|
||||||
|
});
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating indicator:', error);
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a message to the application server.
|
* Sends a message to the application server.
|
||||||
* @param {string} messageType - The type of the message.
|
* @param {string} messageType - The type of the message.
|
||||||
|
|
|
||||||
|
|
@ -468,12 +468,12 @@ class Indicators {
|
||||||
const nameDiv = row.querySelector('div:nth-child(2)'); // Second <div> contains the name
|
const nameDiv = row.querySelector('div:nth-child(2)'); // Second <div> contains the name
|
||||||
const indicatorName = nameDiv.innerText.trim(); // Get the indicator name
|
const indicatorName = nameDiv.innerText.trim(); // Get the indicator name
|
||||||
|
|
||||||
// Gather input data
|
|
||||||
// Initialize formObj with the name of the indicator
|
// Initialize formObj with the name of the indicator
|
||||||
const formObj = {
|
const formObj = {
|
||||||
name: indicatorName,
|
name: indicatorName,
|
||||||
visible: false, // Default value for visible (will be updated based on the checkbox input)
|
visible: false, // Default value for visible (will be updated based on the checkbox input)
|
||||||
properties: {}
|
source: {}, // Initialize the source object directly at the top level
|
||||||
|
properties: {} // Initialize the properties object
|
||||||
};
|
};
|
||||||
|
|
||||||
// Iterate over each input (text, checkbox, select) and add its name and value to formObj
|
// Iterate over each input (text, checkbox, select) and add its name and value to formObj
|
||||||
|
|
@ -481,6 +481,9 @@ class Indicators {
|
||||||
if (input.name === 'visible') {
|
if (input.name === 'visible') {
|
||||||
// Handle the visible checkbox separately
|
// Handle the visible checkbox separately
|
||||||
formObj.visible = input.checked;
|
formObj.visible = input.checked;
|
||||||
|
} else if (input.name === 'market' || input.name === 'timeframe' || input.name === 'exchange') {
|
||||||
|
// Directly map inputs to source object fields
|
||||||
|
formObj.source[input.name] = input.value;
|
||||||
} else {
|
} else {
|
||||||
// Add all other inputs (type, period, color) to the properties object
|
// Add all other inputs (type, period, color) to the properties object
|
||||||
formObj.properties[input.name] = input.type === 'checkbox' ? input.checked : input.value;
|
formObj.properties[input.name] = input.type === 'checkbox' ? input.checked : input.value;
|
||||||
|
|
@ -500,11 +503,18 @@ class Indicators {
|
||||||
|
|
||||||
add_to_list(){
|
add_to_list(){
|
||||||
// Adds user input to a list and displays it in a HTML element.
|
// Adds user input to a list and displays it in a HTML element.
|
||||||
// Used in the Create indicator panel (!)Called from inline html.
|
// Collect the property name and value input by the user
|
||||||
let n = document.getElementById("new_prop_name").value;
|
let n = document.getElementById("new_prop_name").value.trim();
|
||||||
let v = document.getElementById("new_prop_value").value;
|
let v = document.getElementById("new_prop_value").value.trim();
|
||||||
|
|
||||||
|
// Ensure the property name and value are not empty
|
||||||
|
if (!n || !v) {
|
||||||
|
alert("Property name and value are required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Converts css color name to hex
|
// Converts css color name to hex
|
||||||
if (n == 'color'){
|
if (n === 'color'){
|
||||||
// list of valid css colors
|
// list of valid css colors
|
||||||
let colours = {
|
let colours = {
|
||||||
"aliceblue":"#f0f8ff", "antiquewhite":"#faebd7", "aqua":"#00ffff", "aquamarine":"#7fffd4", "azure":"#f0ffff", "beige":"#f5f5dc", "bisque":"#ffe4c4", "black":"#000000", "blanchedalmond":"#ffebcd", "blue":"#0000ff", "blueviolet":"#8a2be2", "brown":"#a52a2a", "burlywood":"#deb887", "cadetblue":"#5f9ea0", "chartreuse":"#7fff00", "chocolate":"#d2691e", "coral":"#ff7f50", "cornflowerblue":"#6495ed", "cornsilk":"#fff8dc", "crimson":"#dc143c", "cyan":"#00ffff", "darkblue":"#00008b", "darkcyan":"#008b8b", "darkgoldenrod":"#b8860b", "darkgray":"#a9a9a9", "darkgreen":"#006400", "darkkhaki":"#bdb76b", "darkmagenta":"#8b008b", "darkolivegreen":"#556b2f", "darkorange":"#ff8c00", "darkorchid":"#9932cc", "darkred":"#8b0000", "darksalmon":"#e9967a", "darkseagreen":"#8fbc8f", "darkslateblue":"#483d8b", "darkslategray":"#2f4f4f", "darkturquoise":"#00ced1", "darkviolet":"#9400d3", "deeppink":"#ff1493", "deepskyblue":"#00bfff", "dimgray":"#696969", "dodgerblue":"#1e90ff", "firebrick":"#b22222", "floralwhite":"#fffaf0", "forestgreen":"#228b22", "fuchsia":"#ff00ff", "gainsboro":"#dcdcdc", "ghostwhite":"#f8f8ff", "gold":"#ffd700", "goldenrod":"#daa520", "gray":"#808080", "green":"#008000", "greenyellow":"#adff2f",
|
"aliceblue":"#f0f8ff", "antiquewhite":"#faebd7", "aqua":"#00ffff", "aquamarine":"#7fffd4", "azure":"#f0ffff", "beige":"#f5f5dc", "bisque":"#ffe4c4", "black":"#000000", "blanchedalmond":"#ffebcd", "blue":"#0000ff", "blueviolet":"#8a2be2", "brown":"#a52a2a", "burlywood":"#deb887", "cadetblue":"#5f9ea0", "chartreuse":"#7fff00", "chocolate":"#d2691e", "coral":"#ff7f50", "cornflowerblue":"#6495ed", "cornsilk":"#fff8dc", "crimson":"#dc143c", "cyan":"#00ffff", "darkblue":"#00008b", "darkcyan":"#008b8b", "darkgoldenrod":"#b8860b", "darkgray":"#a9a9a9", "darkgreen":"#006400", "darkkhaki":"#bdb76b", "darkmagenta":"#8b008b", "darkolivegreen":"#556b2f", "darkorange":"#ff8c00", "darkorchid":"#9932cc", "darkred":"#8b0000", "darksalmon":"#e9967a", "darkseagreen":"#8fbc8f", "darkslateblue":"#483d8b", "darkslategray":"#2f4f4f", "darkturquoise":"#00ced1", "darkviolet":"#9400d3", "deeppink":"#ff1493", "deepskyblue":"#00bfff", "dimgray":"#696969", "dodgerblue":"#1e90ff", "firebrick":"#b22222", "floralwhite":"#fffaf0", "forestgreen":"#228b22", "fuchsia":"#ff00ff", "gainsboro":"#dcdcdc", "ghostwhite":"#f8f8ff", "gold":"#ffd700", "goldenrod":"#daa520", "gray":"#808080", "green":"#008000", "greenyellow":"#adff2f",
|
||||||
|
|
@ -512,28 +522,52 @@ class Indicators {
|
||||||
"palegreen":"#98fb98", "paleturquoise":"#afeeee", "palevioletred":"#d87093", "papayawhip":"#ffefd5", "peachpuff":"#ffdab9", "peru":"#cd853f", "pink":"#ffc0cb", "plum":"#dda0dd", "powderblue":"#b0e0e6", "purple":"#800080", "rebeccapurple":"#663399", "red":"#ff0000", "rosybrown":"#bc8f8f", "royalblue":"#4169e1", "saddlebrown":"#8b4513", "salmon":"#fa8072", "sandybrown":"#f4a460", "seagreen":"#2e8b57", "seashell":"#fff5ee", "sienna":"#a0522d", "silver":"#c0c0c0", "skyblue":"#87ceeb", "slateblue":"#6a5acd", "slategray":"#708090", "snow":"#fffafa", "springgreen":"#00ff7f", "steelblue":"#4682b4", "tan":"#d2b48c", "teal":"#008080", "thistle":"#d8bfd8", "tomato":"#ff6347", "turquoise":"#40e0d0", "violet":"#ee82ee", "wheat":"#f5deb3", "white":"#ffffff", "whitesmoke":"#f5f5f5", "yellow":"#ffff00", "yellowgreen":"#9acd32"
|
"palegreen":"#98fb98", "paleturquoise":"#afeeee", "palevioletred":"#d87093", "papayawhip":"#ffefd5", "peachpuff":"#ffdab9", "peru":"#cd853f", "pink":"#ffc0cb", "plum":"#dda0dd", "powderblue":"#b0e0e6", "purple":"#800080", "rebeccapurple":"#663399", "red":"#ff0000", "rosybrown":"#bc8f8f", "royalblue":"#4169e1", "saddlebrown":"#8b4513", "salmon":"#fa8072", "sandybrown":"#f4a460", "seagreen":"#2e8b57", "seashell":"#fff5ee", "sienna":"#a0522d", "silver":"#c0c0c0", "skyblue":"#87ceeb", "slateblue":"#6a5acd", "slategray":"#708090", "snow":"#fffafa", "springgreen":"#00ff7f", "steelblue":"#4682b4", "tan":"#d2b48c", "teal":"#008080", "thistle":"#d8bfd8", "tomato":"#ff6347", "turquoise":"#40e0d0", "violet":"#ee82ee", "wheat":"#f5deb3", "white":"#ffffff", "whitesmoke":"#f5f5f5", "yellow":"#ffff00", "yellowgreen":"#9acd32"
|
||||||
};
|
};
|
||||||
// if the value is in the list of colors convert it.
|
// if the value is in the list of colors convert it.
|
||||||
if (typeof colours[v.toLowerCase()] != 'undefined')
|
if (v.toLowerCase() in colours) {
|
||||||
v = colours[v.toLowerCase()];
|
v = colours[v.toLowerCase()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let p={};
|
|
||||||
p[n] = v;
|
|
||||||
if (document.getElementById("new_prop_list").innerHTML ==""){
|
|
||||||
document.getElementById("new_prop_list").insertAdjacentHTML('beforeend', JSON.stringify(p));
|
|
||||||
}else{
|
|
||||||
document.getElementById("new_prop_list").insertAdjacentHTML('beforeend', ',' + JSON.stringify(p)); }
|
|
||||||
}
|
}
|
||||||
|
// Create a new property row with a clear button
|
||||||
|
const newPropHTML = `
|
||||||
|
<div class="property-item" style="display:flex;align-items:center;">
|
||||||
|
<button style="color:darkred;margin-right:5px;" type="button" onclick="UI.indicators.remove_prop(this)">✘</button>
|
||||||
|
<span>${n}: ${v}</span>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
// Insert the new property into the property list
|
||||||
|
document.getElementById("new_prop_list").insertAdjacentHTML('beforeend', newPropHTML);
|
||||||
|
|
||||||
|
// Update the hidden property object with the new property
|
||||||
|
const propObj = JSON.parse(document.getElementById("new_prop_obj").value || '{}');
|
||||||
|
propObj[n] = v;
|
||||||
|
document.getElementById("new_prop_obj").value = JSON.stringify(propObj);
|
||||||
|
|
||||||
|
// Clear the input fields
|
||||||
|
document.getElementById("new_prop_name").value = '';
|
||||||
|
document.getElementById("new_prop_value").value = '';
|
||||||
|
}
|
||||||
|
// Function to remove a property from the list
|
||||||
|
remove_prop(buttonElement) {
|
||||||
|
const propertyDiv = buttonElement.parentElement;
|
||||||
|
const propertyText = propertyDiv.querySelector('span').textContent;
|
||||||
|
const [propName] = propertyText.split(':');
|
||||||
|
|
||||||
|
// Remove the property div from the DOM
|
||||||
|
propertyDiv.remove();
|
||||||
|
|
||||||
|
// Remove the property from the hidden input object
|
||||||
|
const propObj = JSON.parse(document.getElementById("new_prop_obj").value || '{}');
|
||||||
|
delete propObj[propName.trim()];
|
||||||
|
document.getElementById("new_prop_obj").value = JSON.stringify(propObj);
|
||||||
|
}
|
||||||
// Call to display Create new signal dialog.
|
// Call to display Create new signal dialog.
|
||||||
open_form() {
|
open_form() {
|
||||||
// Show the form
|
// Show the form
|
||||||
document.getElementById("new_ind_form").style.display = "grid";
|
document.getElementById("new_ind_form").style.display = "grid";
|
||||||
|
|
||||||
// Prefill the form fields with the current chart data (if available)
|
// Prefill the form fields with the current chart data (if available)
|
||||||
const marketField = document.querySelector('[name="ei_symbol"]');
|
const marketField = document.getElementById('ei_symbol');
|
||||||
const timeframeField = document.querySelector('[name="ei_timeframe"]');
|
const timeframeField = document.getElementById('ei_timeframe');
|
||||||
const exchangeField = document.querySelector('[name="ei_exchange_name"]');
|
const exchangeField = document.getElementById('ei_exchange_name');
|
||||||
|
|
||||||
// Set default values if fields are empty
|
// Set default values if fields are empty
|
||||||
if (!marketField.value) {
|
if (!marketField.value) {
|
||||||
|
|
@ -553,11 +587,11 @@ class Indicators {
|
||||||
Used in the create indicator panel.*/
|
Used in the create indicator panel.*/
|
||||||
|
|
||||||
// Perform validation
|
// Perform validation
|
||||||
const name = document.querySelector('[name="newi_name"]').value;
|
const name = document.getElementById('newi_name').value;
|
||||||
const type = document.querySelector('[name="newi_type"]').value;
|
const type = document.getElementById('newi_type').value;
|
||||||
let market = document.querySelector('[name="ei_symbol"]').value;
|
let market = document.getElementById('ei_symbol').value;
|
||||||
const timeframe = document.querySelector('[name="ei_timeframe"]').value;
|
const timeframe = document.getElementById('ei_timeframe').value;
|
||||||
const exchange = document.querySelector('[name="ei_exchange_name"]').value;
|
const exchange = document.getElementById('ei_exchange_name').value;
|
||||||
|
|
||||||
let errorMsg = '';
|
let errorMsg = '';
|
||||||
|
|
||||||
|
|
@ -568,8 +602,7 @@ class Indicators {
|
||||||
errorMsg += 'Indicator type is required.\n';
|
errorMsg += 'Indicator type is required.\n';
|
||||||
}
|
}
|
||||||
if (!market) {
|
if (!market) {
|
||||||
market = window.UI.data.trading_pair;
|
errorMsg += 'Indicator market is required.\n';
|
||||||
document.querySelector('[name="ei_symbol"]').value = market; // Set the form field
|
|
||||||
}
|
}
|
||||||
if (!timeframe) {
|
if (!timeframe) {
|
||||||
errorMsg += 'Timeframe is required.\n';
|
errorMsg += 'Timeframe is required.\n';
|
||||||
|
|
@ -583,13 +616,24 @@ class Indicators {
|
||||||
return; // Stop form submission if there are errors
|
return; // Stop form submission if there are errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// If validation passes, proceed with form submission
|
// Collect and update properties
|
||||||
let pl = document.getElementById("new_prop_list").innerHTML;
|
const propObj = {}
|
||||||
if (pl) {
|
|
||||||
pl = '[' + pl + ']';
|
// Optionally add name, type, market, timeframe, and exchange to the properties if needed
|
||||||
|
propObj["name"] = name;
|
||||||
|
propObj["type"] = type;
|
||||||
|
propObj["source"] = {'market':market,'timeframe': timeframe,'exchange':exchange};
|
||||||
|
propObj["properties"] = document.getElementById("new_prop_obj").value;
|
||||||
|
|
||||||
|
// Call comms to send data to the server
|
||||||
|
this.comms.submitIndicator(propObj).then(response => {
|
||||||
|
if (response.success) {
|
||||||
|
window.location.reload(); // This triggers a full page refresh
|
||||||
|
} else {
|
||||||
|
alert('Failed to create a new Indicator.');
|
||||||
}
|
}
|
||||||
document.getElementById("new_prop_obj").value = pl;
|
});
|
||||||
document.getElementById("new_i_form").submit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,55 +6,41 @@
|
||||||
<div class="section" style="grid-column: 1;">
|
<div class="section" style="grid-column: 1;">
|
||||||
<button class="btn" onclick="UI.indicators.open_form()">New Indicator</button>
|
<button class="btn" onclick="UI.indicators.open_form()">New Indicator</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Button for displaying indicator options -->
|
<!-- Button for displaying indicator options -->
|
||||||
<div class="section" style="grid-column: 2;">
|
<div class="section" style="grid-column: 2;">
|
||||||
<button class="btn">Indicator Options</button>
|
<button class="btn">Indicator Options</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Section for displaying the list of indicators -->
|
<!-- Indicator list display section -->
|
||||||
<div class="section" style="margin-top: 15px; overflow-x: auto; display: grid; grid-auto-rows: minmax(80px, auto); grid-template-columns: 75px 200px 150px 100px 75px 100px 100px auto; gap: 10px; padding-left: 10px;">
|
<div class="section" style="margin-top: 15px; overflow-x: auto; display: grid; grid-auto-rows: minmax(80px, auto); grid-template-columns: 75px 200px 100px 150px 150px 75px 100px 100px auto; gap: 10px; padding-left: 10px;">
|
||||||
<!-- Background color change for this section -->
|
<div style="background-color: #F7E1C1; grid-column: 1 / span 9; padding: 5px 0; display: grid; grid-template-columns: 75px 200px 100px 150px 150px 75px 100px 100px auto; gap: 10px;">
|
||||||
<div style="background-color: #F7E1C1; grid-column: 1 / span 8; padding: 5px 0; display: grid; grid-template-columns: 75px 200px 150px 100px 75px 100px 100px auto; gap: 10px;">
|
|
||||||
<div style="text-align: center;"><h3 class="header-text">Remove or Edit</h3></div>
|
<div style="text-align: center;"><h3 class="header-text">Remove or Edit</h3></div>
|
||||||
<div style="text-align: center;"><h3 class="header-text">Name</h3></div>
|
<div style="text-align: center;"><h3 class="header-text">Name</h3></div>
|
||||||
<div style="text-align: center;"><h3 class="header-text">Type</h3></div>
|
|
||||||
<div style="text-align: center;"><h3 class="header-text">Value</h3></div>
|
<div style="text-align: center;"><h3 class="header-text">Value</h3></div>
|
||||||
|
<div style="text-align: center;"><h3 class="header-text">Type</h3></div>
|
||||||
|
<div style="text-align: center;"><h3 class="header-text">Source</h3></div>
|
||||||
<div style="text-align: center;"><h3 class="header-text">Visible</h3></div>
|
<div style="text-align: center;"><h3 class="header-text">Visible</h3></div>
|
||||||
<div style="text-align: center;"><h3 class="header-text">Period</h3></div>
|
<div style="text-align: center;"><h3 class="header-text">Period</h3></div>
|
||||||
<div style="text-align: center;"><h3 class="header-text">Color</h3></div>
|
<div style="text-align: center;"><h3 class="header-text">Color</h3></div>
|
||||||
<div style="text-align: center;"><h3 class="header-text">Properties</h3></div>
|
<div style="text-align: center;"><h3 class="header-text">Properties</h3></div>
|
||||||
<hr style="grid-column: 1 / span 8;width: 100%; height: 1px; border: 1px solid #ccc; margin: 5px 0;">
|
<hr style="grid-column: 1 / span 9; width: 100%; height: 1px; border: 1px solid #ccc; margin: 5px 0;">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Loop through each indicator in indicator_list -->
|
<!-- Loop through each indicator in indicator_list -->
|
||||||
{% for indicator in indicator_list %}
|
{% for indicator in indicator_list %}
|
||||||
<div class="indicator-row" style="display: grid; grid-column: 1 / span 8; align-items: center; grid-template-columns: 75px 200px 150px 100px 75px 100px 100px auto; gap: 10px; padding-left: 10px;">
|
<div class="indicator-row" style="display: grid; grid-column: 1 / span 9; align-items: center; grid-template-columns: 75px 200px 100px 150px 150px 75px 100px 100px auto; gap: 10px; padding-left: 10px;">
|
||||||
|
|
||||||
<!-- Buttons to delete or update the indicator -->
|
<!-- Edit and Remove buttons -->
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
<button type="button" class="e_btn" onclick="UI.indicators.deleteIndicator('{{indicator}}', event)">✘</button>
|
<button type="button" class="e_btn" onclick="UI.indicators.deleteIndicator('{{indicator}}', event)">✘</button>
|
||||||
<button type="button" class="e_btn" style="color:darkgreen;" onclick="UI.indicators.updateIndicator(event)">✔</button>
|
<button type="button" class="e_btn edit" onclick="UI.indicators.updateIndicator(event)">✔</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Display the indicator name -->
|
<!-- Indicator Name -->
|
||||||
<div style="text-align: center;">{{indicator}}</div>
|
<div style="text-align: center;">{{indicator}}</div>
|
||||||
|
|
||||||
<!-- Fixed property: Type (Dropdown) -->
|
<!-- Fixed property: Value -->
|
||||||
<div style="text-align: center;">
|
|
||||||
{% if 'type' in indicator_list[indicator] %}
|
|
||||||
<select class="ietextbox" id="{{indicator}}_type" name="type">
|
|
||||||
{% for i_type in indicator_types %}
|
|
||||||
<option value="{{i_type}}" {% if indicator_list[indicator]['type'] == i_type %} selected="selected"{% endif %}>{{i_type}}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
{% else %}
|
|
||||||
<span>-</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Fixed property: Value (Readonly Number) -->
|
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
{% if 'value' in indicator_list[indicator] %}
|
{% if 'value' in indicator_list[indicator] %}
|
||||||
<input class="ie_value" type="number" id="{{indicator}}_value" value="{{indicator_list[indicator]['value']}}" name="value" readonly>
|
<input class="ie_value" type="number" id="{{indicator}}_value" value="{{indicator_list[indicator]['value']}}" name="value" readonly>
|
||||||
|
|
@ -63,16 +49,49 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fixed property: Visible (Checkbox) -->
|
<!-- Fixed property: Type -->
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
{% if 'visible' in indicator_list[indicator] %}
|
{% if 'type' in indicator_list[indicator] %}
|
||||||
<input class="ietextbox" type="checkbox" id="{{indicator}}_visible" value="{{indicator_list[indicator]['visible']}}" name="visible" {% if indicator in checked %} checked {% endif %}>
|
<span id="{{indicator}}_type">{{indicator_list[indicator]['type']}}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span>-</span>
|
<span>-</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fixed property: Period (Number Input) -->
|
<!-- Source fields for Symbol, Timeframe, Exchange -->
|
||||||
|
<div style="text-align: center;">
|
||||||
|
<input list="symbols" class="ietextbox" id="{{indicator}}_source_symbol" name="market" value="{{indicator_list[indicator]['source']['market']}}">
|
||||||
|
<datalist id="symbols">
|
||||||
|
{% for symbol in symbols %}
|
||||||
|
<option value="{{symbol}}"></option>
|
||||||
|
{% endfor %}
|
||||||
|
</datalist>
|
||||||
|
|
||||||
|
<input list="timeframes" class="ietextbox" id="{{indicator}}_source_timeframe" name="timeframe" value="{{indicator_list[indicator]['source']['timeframe']}}">
|
||||||
|
<datalist id="timeframes">
|
||||||
|
{% for timeframe in intervals %}
|
||||||
|
<option value="{{timeframe}}"></option>
|
||||||
|
{% endfor %}
|
||||||
|
</datalist>
|
||||||
|
|
||||||
|
<input list="exchanges" class="ietextbox" id="{{indicator}}_source_exchange_name" name="exchange" value="{{indicator_list[indicator]['source']['exchange']}}">
|
||||||
|
<datalist id="exchanges">
|
||||||
|
{% for exchange in exchanges %}
|
||||||
|
<option value="{{exchange}}"></option>
|
||||||
|
{% endfor %}
|
||||||
|
</datalist>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Checkbox for Visibility -->
|
||||||
|
<div style="text-align: center;">
|
||||||
|
{% if 'visible' in indicator_list[indicator] %}
|
||||||
|
<input type="checkbox" id="{{indicator}}_visible" value="{{indicator_list[indicator]['visible']}}" name="visible" {% if indicator in checked %} checked {% endif %}>
|
||||||
|
{% else %}
|
||||||
|
<span>-</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Period field -->
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
{% if 'period' in indicator_list[indicator] %}
|
{% if 'period' in indicator_list[indicator] %}
|
||||||
<input class="ietextbox" type="number" id="{{indicator}}_period" value="{{indicator_list[indicator]['period']}}" name="period">
|
<input class="ietextbox" type="number" id="{{indicator}}_period" value="{{indicator_list[indicator]['period']}}" name="period">
|
||||||
|
|
@ -81,7 +100,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fixed property: Color (Color Picker) -->
|
<!-- Color Picker -->
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
{% if 'color' in indicator_list[indicator] %}
|
{% if 'color' in indicator_list[indicator] %}
|
||||||
<input class="ietextbox" type="color" id="{{indicator}}_color" value="{{indicator_list[indicator]['color']}}" name="color">
|
<input class="ietextbox" type="color" id="{{indicator}}_color" value="{{indicator_list[indicator]['color']}}" name="color">
|
||||||
|
|
@ -90,17 +109,15 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Dynamic properties (remaining ones not covered by known properties) -->
|
<!-- Dynamic properties -->
|
||||||
<div style="grid-column: span 1; text-align: left; display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">
|
<div style="grid-column: span 1; text-align: left; display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">
|
||||||
{% for property, value in indicator_list[indicator].items() %}
|
{% for property, value in indicator_list[indicator].items() %}
|
||||||
{% if property not in ['type', 'value', 'color', 'period', 'visible'] %}
|
{% if property not in ['type', 'value', 'color', 'period', 'visible', 'source'] %}
|
||||||
<div style="margin-bottom: 17px;">
|
<div style="margin-bottom: 17px;">
|
||||||
<label for="{{indicator}}_{{property}}" class="ietextbox-label">{{property}}</label>
|
<label for="{{indicator}}_{{property}}" class="ietextbox-label">{{property}}</label>
|
||||||
{% if 'color' in property %}
|
{% if 'color' in property %}
|
||||||
<!-- Color picker for properties with 'color' in the name -->
|
|
||||||
<input class="ietextbox" type="color" id="{{indicator}}_{{property}}" value="{{value}}" name="{{property}}">
|
<input class="ietextbox" type="color" id="{{indicator}}_{{property}}" value="{{value}}" name="{{property}}">
|
||||||
{% else %}
|
{% else %}
|
||||||
<!-- Regular text input for other properties -->
|
|
||||||
<input class="ietextbox" type="text" id="{{indicator}}_{{property}}" value="{{value}}" name="{{property}}">
|
<input class="ietextbox" type="text" id="{{indicator}}_{{property}}" value="{{value}}" name="{{property}}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -111,29 +128,68 @@
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Styles -->
|
||||||
<style>
|
<style>
|
||||||
.section .column {
|
/* General shadow styling for all elements except checkboxes */
|
||||||
padding: 10px;
|
.ietextbox, .e_btn, input[type="color"] {
|
||||||
max-width: 200px; /* Set max-width for all columns */
|
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.3);
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section h3 {
|
/* Style for Edit and Remove buttons */
|
||||||
margin-bottom: 0;
|
.e_btn {
|
||||||
margin-top: 0;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: #f44336; /* Red for remove */
|
||||||
|
color: white;
|
||||||
|
border: 1px solid black;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section .buttons-column {
|
/* Green background for Edit button */
|
||||||
max-width: 75px; /* Buttons column */
|
.e_btn.edit {
|
||||||
|
background-color: #4CAF50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section .visible-column {
|
/* Hover effect for buttons */
|
||||||
max-width: 75px; /* Visible checkbox column */
|
.e_btn:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
box-shadow: 0px 6px 8px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Alternate row background colors */
|
.e_btn:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Color picker input */
|
||||||
|
input[type="color"] {
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 5px;
|
||||||
|
width: 60px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 17px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover effect for color picker */
|
||||||
|
input[type="color"]:hover {
|
||||||
|
box-shadow: 0px 6px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styling for input fields and textboxes */
|
||||||
|
.ietextbox {
|
||||||
|
width: 100%;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alternating row colors */
|
||||||
.indicator-row:nth-child(even) {
|
.indicator-row:nth-child(even) {
|
||||||
background-color: #E0E0E0;
|
background-color: #E0E0E0;
|
||||||
}
|
}
|
||||||
|
|
@ -142,14 +198,42 @@
|
||||||
background-color: #F7E1C1;
|
background-color: #F7E1C1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bold labels with drop shadow for dynamic properties */
|
/* Opposite colors for textboxes */
|
||||||
.ietextbox-label {
|
.indicator-row:nth-child(even) .ietextbox {
|
||||||
font-weight: bold;
|
background-color: #F7E1C1;
|
||||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Drop shadow for header text */
|
.indicator-row:nth-child(odd) .ietextbox {
|
||||||
.header-text {
|
background-color: #E0E0E0;
|
||||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
}
|
||||||
|
input[type="checkbox"] {
|
||||||
|
width: 20px; /* Increase the size of the checkbox */
|
||||||
|
height: 20px; /* Adjust the height to match */
|
||||||
|
cursor: pointer; /* Add a pointer cursor for better UX */
|
||||||
|
margin: 0; /* Reset margin */
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- JavaScript -->
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Handle focus and restore behavior for each dropdown
|
||||||
|
document.querySelectorAll('.ietextbox').forEach(function(input) {
|
||||||
|
let originalValue = input.value;
|
||||||
|
|
||||||
|
// Clear the input when focused
|
||||||
|
input.addEventListener('focus', function() {
|
||||||
|
originalValue = input.value;
|
||||||
|
input.value = ''; // Clear the input
|
||||||
|
});
|
||||||
|
|
||||||
|
// Restore the original value if no new selection is made
|
||||||
|
input.addEventListener('blur', function() {
|
||||||
|
if (!input.value) {
|
||||||
|
input.value = originalValue; // Restore the original value if input is empty
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,72 @@
|
||||||
<div class="form-popup" id="new_ind_form" style="overflow: auto;">
|
<div class="form-popup" id="new_ind_form" style="overflow: auto;">
|
||||||
<form id="new_i_form" action="/settings" method="POST" class="form-container" style="display: grid; grid-template-columns: 1fr; grid-template-rows: auto;">
|
<div id="new_i_form" class="form-container" style="display: grid; grid-template-columns: 1fr; grid-template-rows: auto;">
|
||||||
<!-- Panel 1 of 1 (5 rows, 2 columns) -->
|
<!-- Panel 1 of 1 (5 rows, 2 columns) -->
|
||||||
<div id="indicator_pan_1" class="form_panels" style="display: grid; grid-template-columns:repeat(2,1fr); grid-template-rows: repeat(5,1fr); gap: 10px;">
|
<div id="indicator_pan_1" class="form_panels" style="display: grid; grid-template-columns:repeat(2,1fr); grid-template-rows: repeat(5,1fr); gap: 10px;">
|
||||||
|
|
||||||
<!-- Panel title (row 1/5)-->
|
<!-- Panel title (row 1/5)-->
|
||||||
<h1 style="grid-column: 1 / span 2; grid-row: 1;">Create New Indicator</h1>
|
<h1 style="grid-column: 1 / span 2; grid-row: 1;">Create New Indicator</h1>
|
||||||
|
|
||||||
<input type="hidden" name="setting" value="new_indicator"/>
|
<!-- Create indicator div container -->
|
||||||
<!-- Create indicator div container --!>
|
|
||||||
<div style="grid-column: 1 / span 2; grid-row: 2 / span 3;">
|
<div style="grid-column: 1 / span 2; grid-row: 2 / span 3;">
|
||||||
<label for "newi_name">Indicator Name</label><input type ="text" name="newi_name" value="New Indicator">
|
<label for="newi_name">Indicator Name</label><input type="text" id="newi_name" value="New Indicator">
|
||||||
<label for "newi_type">Type</label>
|
<label for="newi_type">Type</label>
|
||||||
<select id="newi_type" name="newi_type">
|
<select id="newi_type">
|
||||||
{% for i_type in indicator_types %}
|
{% for i_type in indicator_types %}
|
||||||
<option value="{{i_type}}">{{i_type}}</option>
|
<option value="{{i_type}}">{{i_type}}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<br><br>
|
<br><br>
|
||||||
<span>Properties: <span id="new_prop_list"></span></span>
|
<span>Properties: <span id="new_prop_list"></span></span>
|
||||||
<button name="add_prop_clear" id="add_prop_clear" style="color:darkred;" type="button" value="add_prop_clear" onclick="document.getElementById('new_prop_list').innerHTML=''">✘</button>
|
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
<div id="add_prop_container" name="add_prop_container">
|
<div id="add_prop_container">
|
||||||
<label for "new_prop_name">Property Name</label>
|
<label for="new_prop_name">Property Name</label>
|
||||||
<input type="text" id="new_prop_name" name="new_prop_name" value="name">
|
<input type="text" id="new_prop_name" name="new_prop_name" list="default_properties" value="name" onclick="this.value='';">
|
||||||
|
<datalist id="default_properties">
|
||||||
|
<option value="period">
|
||||||
|
<option value="devup">
|
||||||
|
<option value="devdn">
|
||||||
|
<option value="ma">
|
||||||
|
<option value="fast_p">
|
||||||
|
<option value="slow_p">
|
||||||
|
<option value="signal_p">
|
||||||
|
<option value="color">
|
||||||
|
<option value="color_1">
|
||||||
|
<option value="color_2">
|
||||||
|
<option value="color_3">
|
||||||
|
</datalist>
|
||||||
<br>
|
<br>
|
||||||
<label for "new_prop_value">Property Value</label>
|
<label for="new_prop_value">Property Value</label>
|
||||||
<input type="text" id="new_prop_value" name="new_prop_value" value="value">
|
<input type="text" id="new_prop_value" name="new_prop_value" value="value" onclick="this.value='';">
|
||||||
<button name="add_prop" id="add_prop" type="button" value="add_prop" style="color:darkgreen;" onclick="UI.indicators.add_to_list()">✙</button>
|
<button id="add_prop" type="button" style="color:darkgreen;" onclick="UI.indicators.add_to_list()">✙</button>
|
||||||
<label for "add_prop">Add Property</label>
|
<label for="add_prop">Add Property</label>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<!-- Trading pair selector -->
|
<!-- Trading pair selector -->
|
||||||
<label for="ei_symbols">Trading Pair</label>
|
<label for="ei_symbols">Trading Pair</label>
|
||||||
<input list="ei_symbols" name="ei_symbol" id="ei_symbol" style="width: 96px;">
|
<input list="ei_symbols" id="ei_symbol" style="width: 96px;">
|
||||||
<datalist id="ei_symbols">
|
<datalist id="ei_symbols">
|
||||||
{% for symbol in symbols %}
|
{% for symbol in symbols %}
|
||||||
<option>{{ symbol }}</option>
|
<option value="{{symbol}}"></option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</datalist>
|
</datalist>
|
||||||
|
|
||||||
<!-- Time timeframe selector -->
|
<!-- Time timeframe selector -->
|
||||||
<select id="ei_timeframe" name="ei_timeframe">
|
<select id="ei_timeframe">
|
||||||
{% for time_frame in intervals %}
|
{% for time_frame in intervals %}
|
||||||
<option {% if time_frame == interval_state %} selected="selected" {% endif %}>{{ time_frame }}</option>
|
<option {% if time_frame == interval_state %} selected="selected" {% endif %}>{{ time_frame }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- Exchange selector -->
|
<!-- Exchange selector -->
|
||||||
<select id="ei_exchange_name" name="ei_exchange_name">
|
<select id="ei_exchange_name">
|
||||||
{% for exchange in exchanges %}
|
{% for exchange in exchanges %}
|
||||||
<option {% if exchange == selected_exchange %} selected="selected" {% endif %}>{{ exchange }}</option>
|
<option {% if exchange == selected_exchange %} selected="selected" {% endif %}>{{ exchange }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<input type="hidden" id="new_prop_obj" name="new_prop_obj" value="">
|
|
||||||
|
<input type="hidden" id="new_prop_obj" value="">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Buttons (row 5/5) -->
|
<!-- Buttons (row 5/5) -->
|
||||||
|
|
@ -61,6 +75,7 @@
|
||||||
<button type="button" class="btn submit" onclick="UI.indicators.submit_new_i()">Create Indicator</button>
|
<button type="button" class="btn submit" onclick="UI.indicators.submit_new_i()">Create Indicator</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div><!----End panel 1--------->
|
</div>
|
||||||
</form>
|
<!-- End panel 1 -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue