implement apps scripts control & technical analyze & charting
This commit is contained in:
0
apps_scripts/__init__.py
Normal file
0
apps_scripts/__init__.py
Normal file
60
apps_scripts/repository.py
Normal file
60
apps_scripts/repository.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from apps_scripts.session import service as apps_scripts_service
|
||||
|
||||
|
||||
|
||||
|
||||
def run_function(deployment_id, name, params=None):
|
||||
request = {
|
||||
'function': name,
|
||||
'devMode': False # Set to False for production
|
||||
}
|
||||
if params:
|
||||
request['parameters'] = params
|
||||
response = apps_scripts_service.scripts().run(body=request, scriptId=deployment_id).execute()
|
||||
return response
|
||||
|
||||
|
||||
def create_project(title):
|
||||
request = {"title": title}
|
||||
response = apps_scripts_service.projects()\
|
||||
.create(body=request)\
|
||||
.execute()
|
||||
return response
|
||||
|
||||
|
||||
def create_script(script_id):
|
||||
SAMPLE_CODE = """
|
||||
function helloWorld() {
|
||||
var spreadsheet = SpreadsheetApp.openById('1-GpXFraefQOEFk2j6zkThk5okd_7vR5JL6ZZ0c0bI_U');
|
||||
var sheet = spreadsheet.getSheetByName('system');
|
||||
var now = new Date();
|
||||
|
||||
sheet.getRange("I1").setValue(now.toLocaleString());
|
||||
Logger.log("Timestamp: " + now.toLocaleString());
|
||||
}
|
||||
function doPost(e) {
|
||||
helloWorld();
|
||||
return ContentService.createTextOutput("Hello World script executed and written to cell I1.");
|
||||
}
|
||||
|
||||
""".strip()
|
||||
|
||||
SAMPLE_MANIFEST = """
|
||||
{
|
||||
"timeZone": "Europe/Istanbul",
|
||||
"exceptionLogging": "CLOUD"
|
||||
}
|
||||
""".strip()
|
||||
|
||||
request = {
|
||||
"files": [
|
||||
{"name": "hello", "type": "SERVER_JS", "source": SAMPLE_CODE},
|
||||
{"name": "appsscript", "type": "JSON", "source": SAMPLE_MANIFEST, },
|
||||
]
|
||||
}
|
||||
response = apps_scripts_service.projects()\
|
||||
.updateContent(body=request, scriptId=script_id)\
|
||||
.execute()
|
||||
|
||||
web_console_url = f"https://script.google.com/d/{response['scriptId']}"
|
||||
return response
|
||||
32
apps_scripts/session.py
Normal file
32
apps_scripts/session.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import os
|
||||
|
||||
|
||||
from google.auth.transport.requests import Request
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from google.oauth2.credentials import Credentials
|
||||
from googleapiclient.discovery import build
|
||||
|
||||
|
||||
from config import envvar
|
||||
|
||||
|
||||
|
||||
def generate_creds():
|
||||
if os.path.exists(envvar.APP_SCRIPT_TOKEN):
|
||||
creds = Credentials.from_authorized_user_file(envvar.APP_SCRIPT_TOKEN, envvar.SCOPES)
|
||||
# If there are no (valid) credentials available, let the user log in.
|
||||
if not creds or not creds.valid:
|
||||
if creds and creds.expired and creds.refresh_token:
|
||||
creds.refresh(Request())
|
||||
else:
|
||||
flow = InstalledAppFlow.from_client_secrets_file(
|
||||
envvar.CLIENT_SECRET, envvar.SCOPES
|
||||
)
|
||||
creds = flow.run_local_server(port=0)
|
||||
with open(envvar.APP_SCRIPT_TOKEN, "w") as token:
|
||||
token.write(creds.to_json())
|
||||
return creds
|
||||
|
||||
|
||||
session_creds = generate_creds()
|
||||
service = build("script", "v1", credentials=session_creds)
|
||||
0
charting/__init__.py
Normal file
0
charting/__init__.py
Normal file
66
charting/technicals.py
Normal file
66
charting/technicals.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from bokeh.plotting import figure, show
|
||||
from bokeh.layouts import column
|
||||
from bokeh.models import Range1d
|
||||
|
||||
|
||||
def generate_macd_chart(data):
|
||||
"""
|
||||
:param data:
|
||||
requires a dataframe like this:
|
||||
# Column Non-Null Count Dtype
|
||||
--- ------ -------------- -----
|
||||
0 Date 239 non-null datetime64[ns, UTC]
|
||||
1 Open 239 non-null float64
|
||||
2 High 239 non-null float64
|
||||
3 Low 239 non-null float64
|
||||
4 Close 239 non-null float64
|
||||
5 Volume 239 non-null int64
|
||||
6 MACD 206 non-null float64
|
||||
7 MACD_Signal 206 non-null float64
|
||||
8 MACD_Hist 206 non-null float64
|
||||
"""
|
||||
# Create the figure for MACD
|
||||
p1 = figure(x_axis_type="datetime", title="MACD", width=800, height=400)
|
||||
p1.line(data['Date'], data['MACD'], color="blue", legend_label="MACD")
|
||||
p1.line(data['Date'], data['MACD_Signal'], color="red", legend_label="Signal")
|
||||
# Add histogram bars for MACD_Hist
|
||||
p1.vbar(x=data['Date'], top=data['MACD_Hist'], width=0.5, color="green", legend_label="Histogram")
|
||||
p1.legend.location = "top_left"
|
||||
p1.legend.click_policy = "hide"
|
||||
# Set the x_range to avoid empty space on the right
|
||||
p1.x_range = Range1d(start=data['Date'].min(), end=data['Date'].max())
|
||||
# Use column layout if you plan to add more plots
|
||||
layout = column(p1, sizing_mode='stretch_width')
|
||||
# Show plot
|
||||
show(layout)
|
||||
|
||||
def generate_macd_with_obv_chart(data):
|
||||
"""
|
||||
:param data:
|
||||
requires a dataframe like this:
|
||||
# Column Non-Null Count Dtype
|
||||
--- ------ -------------- -----
|
||||
0 Date 239 non-null datetime64[ns, UTC]
|
||||
1 Open 239 non-null float64
|
||||
2 High 239 non-null float64
|
||||
3 Low 239 non-null float64
|
||||
4 Close 239 non-null float64
|
||||
5 Volume 239 non-null int64
|
||||
6 MACD 206 non-null float64
|
||||
7 MACD_Signal 206 non-null float64
|
||||
8 MACD_Hist 206 non-null float64
|
||||
9 OBV 239 non-null float64
|
||||
:return: None
|
||||
"""
|
||||
# Create the MACD chart
|
||||
p1 = figure(x_axis_type="datetime", title="MACD", width=800, height=400)
|
||||
p1.line(data['Date'], data['MACD'], color="blue", legend_label="MACD")
|
||||
p1.line(data['Date'], data['MACD_Signal'], color="red", legend_label="Signal")
|
||||
p1.vbar(x=data['Date'], top=data['MACD_Hist'], width=0.5, color="green", legend_label="Histogram")
|
||||
p1.legend.location = "top_left"
|
||||
# Create the OBV chart
|
||||
p2 = figure(x_axis_type="datetime", title="On-Balance Volume", width=800, height=200)
|
||||
p2.line(data['Date'], data['OBV'], color="orange", legend_label="OBV")
|
||||
p2.legend.location = "top_left"
|
||||
# Show the charts together
|
||||
show(column(p1, p2))
|
||||
47
dev.py
Normal file
47
dev.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import talib
|
||||
from loguru import logger
|
||||
|
||||
from gsheets_client import repository as gsheets
|
||||
from providers.provider import FinDataRetriever
|
||||
from providers.sources.yfin import YFinanceProvider
|
||||
from charting.technicals import generate_macd_with_obv_chart
|
||||
|
||||
|
||||
ticker = "BTC-USD"
|
||||
retriever = FinDataRetriever(provider=YFinanceProvider(), ticker=ticker)
|
||||
|
||||
ss_system = gsheets.get_sheet(sheet_name="system")
|
||||
_, date_range = gsheets.get_date_range(sheet=ss_system)
|
||||
cell_start = ss_system.acell(date_range[1])
|
||||
cell_end = ss_system.acell(date_range[0])
|
||||
data_diff = cell_start.row - cell_end.row
|
||||
end_date = datetime.strptime(cell_end.value, '%Y-%m-%d')
|
||||
## DIRTYFIX:
|
||||
# historical ile current arası boşluk, 1 gün daha geriden indic date başlatıyor
|
||||
end_date += timedelta(days=1)
|
||||
##
|
||||
start_date = end_date - timedelta(days=data_diff)
|
||||
|
||||
df = retriever.get_historical(start=start_date, end=end_date)
|
||||
df.drop(columns=['Dividends', 'Stock Splits'], inplace=True)
|
||||
|
||||
## to technical anlaysis calculations here
|
||||
df['MACD'], df['MACD_Signal'], df['MACD_Hist'] = talib.MACD(df['Close'])
|
||||
df['OBV'] = talib.OBV(df['Close'], df['Volume'])
|
||||
|
||||
# parse: Get only technical indicator related column names, convert them to lowercase, and concatenate the prefix 'btcusd_'
|
||||
ticker_header = retriever.provider.convert_ticker_symbol_to_gsheets_header(ticker)
|
||||
columns_indics = ["MACD", "MACD_Signal", "MACD_Hist", "OBV"]
|
||||
indicators = [f'{ticker_header}_{col.lower()}' for col in df.columns if col in columns_indics]
|
||||
|
||||
# update: write to sheets
|
||||
for indicator, columns_indic in zip(indicators, columns_indics):
|
||||
gsheets.update_historical_indicator(sheet=ss_system, indicator=indicator, data=df[columns_indic])
|
||||
|
||||
## web-based charting
|
||||
df.reset_index(inplace=True)
|
||||
generate_macd_with_obv_chart(data=df)
|
||||
print()
|
||||
|
||||
@@ -30,8 +30,29 @@ def update_date_column(sheet, num_days):
|
||||
sheet.update(values=dates[1:], range_name=start_cell.address, value_input_option=ValueInputOption.user_entered)
|
||||
|
||||
def update_historical(sheet, ticker, data: dict):
|
||||
"""
|
||||
:param sheet:
|
||||
sheets gspread object
|
||||
:param ticker:
|
||||
ticker header "btcusd" as in system sheet
|
||||
:param data:
|
||||
{"close": values_close, "date": values_dates}
|
||||
:return:
|
||||
"""
|
||||
logger.debug(f"updating historical values for {ticker}")
|
||||
header = sheet.find(ticker)
|
||||
start_cell = Cell(header.row + 3, header.col)
|
||||
sheet.update(values=data["close"], range_name=start_cell.address, value_input_option=ValueInputOption.user_entered)
|
||||
|
||||
def update_historical_indicator(sheet, indicator, data):
|
||||
logger.debug(f"updating historical singleindic values for {indicator}")
|
||||
data.fillna(0, inplace=True) # https://stackoverflow.com/questions/76042113/gspread-error-when-inserting-to-google-sheet-out-of-range-float-values-are-not
|
||||
|
||||
header = sheet.find(indicator)
|
||||
start_cell = Cell(header.row + 3, header.col)
|
||||
|
||||
values_dates = list(reversed([[date.strftime('%Y-%m-%d')] for date in data.index.to_list()]))
|
||||
values_indicator = list(reversed([[round(value,2)] for value in data.values.tolist()]))
|
||||
|
||||
sheet.update(values=values_indicator, range_name=start_cell.address, value_input_option=ValueInputOption.user_entered)
|
||||
sheet.update(values=values_dates, range_name="I5", value_input_option=ValueInputOption.user_entered)
|
||||
|
||||
@@ -12,7 +12,6 @@ idna==3.8
|
||||
loguru==0.7.2
|
||||
lxml==5.3.0
|
||||
multitasking==0.0.11
|
||||
numpy==2.1.0
|
||||
oauthlib==3.2.2
|
||||
pandas==2.2.2
|
||||
peewee==3.17.6
|
||||
@@ -24,6 +23,7 @@ pytz==2024.1
|
||||
requests==2.32.3
|
||||
requests-oauthlib==2.0.0
|
||||
rsa==4.9
|
||||
setuptools==74.0.0
|
||||
six==1.16.0
|
||||
soupsieve==2.6
|
||||
tzdata==2024.1
|
||||
|
||||
Reference in New Issue
Block a user