diff --git a/backend_copy/__init__.py b/backend_copy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend_copy/__pycache__/crud.cpython-38.pyc b/backend_copy/__pycache__/crud.cpython-38.pyc new file mode 100644 index 0000000..3916cfa Binary files /dev/null and b/backend_copy/__pycache__/crud.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/database.cpython-38.pyc b/backend_copy/__pycache__/database.cpython-38.pyc new file mode 100644 index 0000000..0cea2cc Binary files /dev/null and b/backend_copy/__pycache__/database.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/db.cpython-38.pyc b/backend_copy/__pycache__/db.cpython-38.pyc new file mode 100644 index 0000000..c4dcf77 Binary files /dev/null and b/backend_copy/__pycache__/db.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/env.cpython-38.pyc b/backend_copy/__pycache__/env.cpython-38.pyc new file mode 100644 index 0000000..a50acda Binary files /dev/null and b/backend_copy/__pycache__/env.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/function.cpython-38.pyc b/backend_copy/__pycache__/function.cpython-38.pyc new file mode 100644 index 0000000..30111a6 Binary files /dev/null and b/backend_copy/__pycache__/function.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/home.cpython-38.pyc b/backend_copy/__pycache__/home.cpython-38.pyc new file mode 100644 index 0000000..acbe5bb Binary files /dev/null and b/backend_copy/__pycache__/home.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/init.cpython-38.pyc b/backend_copy/__pycache__/init.cpython-38.pyc new file mode 100644 index 0000000..e9c3d9c Binary files /dev/null and b/backend_copy/__pycache__/init.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/login.cpython-38.pyc b/backend_copy/__pycache__/login.cpython-38.pyc new file mode 100644 index 0000000..439e0b2 Binary files /dev/null and b/backend_copy/__pycache__/login.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/main.cpython-38.pyc b/backend_copy/__pycache__/main.cpython-38.pyc new file mode 100644 index 0000000..3249082 Binary files /dev/null and b/backend_copy/__pycache__/main.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/models.cpython-38.pyc b/backend_copy/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000..57bb021 Binary files /dev/null and b/backend_copy/__pycache__/models.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/position.cpython-38.pyc b/backend_copy/__pycache__/position.cpython-38.pyc new file mode 100644 index 0000000..23288bf Binary files /dev/null and b/backend_copy/__pycache__/position.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/strategy.cpython-38.pyc b/backend_copy/__pycache__/strategy.cpython-38.pyc new file mode 100644 index 0000000..a6fab68 Binary files /dev/null and b/backend_copy/__pycache__/strategy.cpython-38.pyc differ diff --git a/backend_copy/__pycache__/test.cpython-38.pyc b/backend_copy/__pycache__/test.cpython-38.pyc new file mode 100644 index 0000000..24ce3b8 Binary files /dev/null and b/backend_copy/__pycache__/test.cpython-38.pyc differ diff --git a/backend_copy/alembic.ini b/backend_copy/alembic.ini new file mode 100644 index 0000000..595fddb --- /dev/null +++ b/backend_copy/alembic.ini @@ -0,0 +1,105 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = migrations + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python-dateutil library that can be +# installed by adding `alembic[tz]` to the pip requirements +# string value is passed to dateutil.tz.gettz() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to migrations/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. Default configuration used for new projects. + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = sqlite:///./bit4coin.db + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/backend_copy/bit4coin.db b/backend_copy/bit4coin.db new file mode 100644 index 0000000..00cc739 Binary files /dev/null and b/backend_copy/bit4coin.db differ diff --git a/backend_copy/crud.py b/backend_copy/crud.py new file mode 100644 index 0000000..9f41e6f --- /dev/null +++ b/backend_copy/crud.py @@ -0,0 +1,32 @@ +from models import User, Item +from datetime import datetime +from database import SessionLocal + +# user_data = User(user_name="hife", +# password="1234", +# balance=67.2, +# api_key="123456789", +# api_secret="qwer", +# create_date=datetime.now()) + + + + +Item_data = Item(symbol = "ETH/USDT", + position_type =1, + enter_time = datetime.now(), + close_time = datetime.now(), + entry_price = 3771.1, + purchase_price = 0.5, + eval_price = 450.2, + eval_PAL = -1.7, + revenue_rate = 2.3, + amount=0.00123, + profit_end = 33901.2, + loss_end=2627.9121, + onwer_id=1 + ) + +db = SessionLocal() +db.add(Item_data) +db.commit() \ No newline at end of file diff --git a/backend_copy/database.py b/backend_copy/database.py new file mode 100644 index 0000000..26ffc24 --- /dev/null +++ b/backend_copy/database.py @@ -0,0 +1,23 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker + + + +SQLALCHEMY_DATABASE_URL = "sqlite:///./bit4coin.db" + +engine = create_engine( + SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} +) +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() + + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/backend_copy/domain/history/__pycache__/history_crud.cpython-38.pyc b/backend_copy/domain/history/__pycache__/history_crud.cpython-38.pyc new file mode 100644 index 0000000..39c47fd Binary files /dev/null and b/backend_copy/domain/history/__pycache__/history_crud.cpython-38.pyc differ diff --git a/backend_copy/domain/history/__pycache__/history_function.cpython-38.pyc b/backend_copy/domain/history/__pycache__/history_function.cpython-38.pyc new file mode 100644 index 0000000..428c5cb Binary files /dev/null and b/backend_copy/domain/history/__pycache__/history_function.cpython-38.pyc differ diff --git a/backend_copy/domain/history/__pycache__/history_router.cpython-38.pyc b/backend_copy/domain/history/__pycache__/history_router.cpython-38.pyc new file mode 100644 index 0000000..096a62e Binary files /dev/null and b/backend_copy/domain/history/__pycache__/history_router.cpython-38.pyc differ diff --git a/backend_copy/domain/history/__pycache__/history_schema.cpython-38.pyc b/backend_copy/domain/history/__pycache__/history_schema.cpython-38.pyc new file mode 100644 index 0000000..c403369 Binary files /dev/null and b/backend_copy/domain/history/__pycache__/history_schema.cpython-38.pyc differ diff --git a/backend_copy/domain/history/history_crud.py b/backend_copy/domain/history/history_crud.py new file mode 100644 index 0000000..d7abe8e --- /dev/null +++ b/backend_copy/domain/history/history_crud.py @@ -0,0 +1,62 @@ +from models import Item +from sqlalchemy.orm import Session + + +from domain.history import history_schema +import datetime + +# 가장 최근에 진입한 포지션의 정보를 불러옴 +def get_item_list_first(db: Session): + item_list_first= db.query(Item).order_by(Item.enter_time.desc()).first() + print(item_list_first) + return [item_list_first] # 리스트 형태 + + +# 가장 최근에 진입한 포지션을 제외한 정보를 불러옴 +def get_item_list(db: Session): + item_list = db.query(Item).order_by(Item.enter_time.desc()).offset(1).all() + print(item_list) + return item_list + + + +# db에 데이터를 넣는 과정 +def create_item(db: Session, item_schema: history_schema.Item_schema): # item_schema의 타입을 올바르게 지정합니다. + Item_data = Item(symbol=item_schema.symbol, + position_type=item_schema.position_type, + enter_time=datetime.datetime.now(), + close_time=datetime.datetime.now(), + entry_price=item_schema.entry_price, + purchase_price=item_schema.purchase_price, + eval_price=item_schema.eval_price, + eval_PAL=item_schema.eval_PAL, + revenue_rate=item_schema.revenue_rate, + amount=item_schema.amount, + profit_end=item_schema.profit_end, + loss_end=item_schema.loss_end, + onwer_id=item_schema.onwer_id) + + db.add(Item_data) + db.commit() + db.refresh(Item_data) + + return Item_data # -> history_function.create_history + +# 가장 상단의 데이터를 포지션 종료할 때 db에 저장 +def update_item_first(db: Session, item_id: int, item_schema: history_schema.Item_schema): + # 가장 상단의 데이터 + item_data = db.query(Item).filter(Item.id == item_id).first() + + # 변동하는 데이터만 가져옴 : 다른 데이터는 변환 없음 + if item_data: + item_data.eval_price = item_schema.eval_price + item_data.eval_PAL = item_schema.eval_PAL + item_data.revenue_rate = item_schema.revenue_rate + + db.commit() + db.refresh(item_data) + + return item_data + else: + return None + diff --git a/backend_copy/domain/history/history_function.py b/backend_copy/domain/history/history_function.py new file mode 100644 index 0000000..c37c63f --- /dev/null +++ b/backend_copy/domain/history/history_function.py @@ -0,0 +1,96 @@ +from domain.history import history_crud, history_schema +from database import get_db +from fastapi import Depends +from sqlalchemy.orm import Session +from strategys.env import profit_percent, loss_percent +from strategys.function import get_cur_price + +from models import Item + +import datetime + + +# history를 자동매매의 내역을 DB로 옮기는 작업 +# strategys.stratefy.strategy_1, 2 , 3의 포지션에 진입하는 if문 안에 들어갈 예정 +def create_history(symbols, position, entry_price, amount, cur_price): + db = next(get_db()) # Depends 대신 직접 세션 객체 생성 + if position["type"] == "long": + profit_end = entry_price + entry_price * profit_percent + loss_end = entry_price - entry_price * loss_percent + elif position["type"] =="short": + profit_end = entry_price - entry_price * profit_percent + loss_end = entry_price + entry_price * profit_percent + + # data 가공 + item_data = { + "symbol": symbols, + "position_type": position["type"], + "enter_time": datetime.datetime.now().isoformat(), + "close_time": datetime.datetime.now().isoformat(), + "entry_price": entry_price, + "cur_price": cur_price, + "purchase_price": amount * entry_price , # amount * current_price + "eval_price": amount * cur_price, # amount * current_price + "eval_PAL": entry_price - cur_price, # entry_price - current_price + "revenue_rate": round(((cur_price - entry_price) / cur_price) * 100, 2) , # round(((entry_price - current_price) / current_price) * 100, 2) + "amount": amount, + "profit_end": profit_end, + "loss_end": loss_end, + "onwer_id": 0 + } + # db에 넣기 위해 우리가 정의한 schema 형식에 맞게 데이터 변환 + item_schema = history_schema.Item_schema(**item_data) + + # db에 데이터를 넣음 + created_item = history_crud.create_item(db, item_schema) + + print(created_item) + +# position 청산할 때 마지막 값을 바탕으로 DB에 업데이트 +# strategys.stratefy.strategy_1, 2 , 3의 포지션을 청산하는 if문 안에 들어갈 예정 +def update_history(cur_price): + db = next(get_db()) # Directly creating a session object + + # Fetch the most recent item from the database + item_data = db.query(Item).order_by(Item.id.desc()).first() + if item_data: + # Calculate new values + eval_price = round(item_data.amount * cur_price, 2) + eval_PAL = round(cur_price - item_data.entry_price, 2) + revenue_rate = round(((cur_price - item_data.entry_price) / cur_price) * 100, 2) + close_time = datetime.datetime.now() + + # data 변환 + item_schema = history_schema.Item_schema( + symbol=item_data.symbol, + position_type=item_data.position_type, + entry_price=item_data.entry_price, + purchase_price=item_data.purchase_price, + eval_price=eval_price, # 변동하는 값 + eval_PAL=eval_PAL, # 변동하는 값 + revenue_rate=revenue_rate, # 변동하는 값 + amount=item_data.amount, + profit_end=item_data.profit_end, + loss_end=item_data.loss_end, + onwer_id=item_data.onwer_id, + enter_time=item_data.enter_time, + close_time=close_time # 변동하는 값 : 종료 시간은 포지션이 종료된 시간으로 정의함 + ) + + # 기존의 db table에 값을 update + updated_item = history_crud.update_item_first(db, item_data.id, item_schema) + print(updated_item) + + return updated_item + else: + return None + + +# history 상단의 내용을 보여주는 함수 db 저장 x +# 변동하는 값들을 db에 저장하지 않고 호출만 함 +def update_item(item, symbol, amount, entry_price): # -> + cur_price = get_cur_price(symbol) + item.eval_price = round(amount * cur_price, 4) + item.eval_PAL = round(entry_price - cur_price, 4) + item.revenue_rate = round(((cur_price - entry_price) / cur_price) * 100, 2) + return item # -> history_router.history_list_first \ No newline at end of file diff --git a/backend_copy/domain/history/history_router.py b/backend_copy/domain/history/history_router.py new file mode 100644 index 0000000..37e55d4 --- /dev/null +++ b/backend_copy/domain/history/history_router.py @@ -0,0 +1,46 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session +from typing import List +from database import get_db +from domain.history import history_schema, history_crud +from models import Item + +from domain.history.history_function import update_item +router = APIRouter( + prefix="/history", +) + + +# 1. history의 main_top은 1초마다 데이터를 들고올 예정 : +# 평가손익, 평가금액, 수익률을 실시간으로 업데이트 할 것(이건 DB에 실시간으로 저장 X) - > 청산할 때 DB에 저장 +# -> 청산이후 list에서 반영된 값을 보여줌 + +# 2. 포지션을 청산할 때 평가손익, 평가금액, 수익률 등의 변동된 값을 업데이트하고 반영해줌 + + +# history의 가장 최근의 item을 보여줌 +@router.get("/list_first", response_model=List[history_schema.Item_schema]) +def history_list_first(db: Session = Depends(get_db)): + _history_list_first = history_crud.get_item_list_first(db) + + # current_price가 필요한 변수들은 DB의 내용을 조금 변화 시켜서 보여줌 + item = _history_list_first[0] + print(item) + _history_list_first = update_item(item, item.symbol, item.amount, item.entry_price) + + return [_history_list_first] # 리스트형태로 반환 - > history.js에서 fetch함 + +# 가장 최근의 item을 제외한 history의 목록을 보여줌 +@router.get("/list", response_model=List[history_schema.Item_schema]) +def history_list(db: Session = Depends(get_db)): + _history_list = history_crud.get_item_list(db) # 상단의 item을 제외한 데이터를 불러옴 + + return _history_list + +# data를 넣는건데 실제로 사용은 안함 +@router.post("/create_item", response_model=history_schema.Item_schema) +def create_item(item_schema: history_schema.Item_schema, db: Session = Depends(get_db)): + _create_item = history_crud.create_item(db, item_schema) + if _create_item is None: + raise HTTPException(status_code=400, detail="Failed to create item") + return _create_item diff --git a/backend_copy/domain/history/history_schema.py b/backend_copy/domain/history/history_schema.py new file mode 100644 index 0000000..42569ff --- /dev/null +++ b/backend_copy/domain/history/history_schema.py @@ -0,0 +1,29 @@ +import datetime + +from pydantic import BaseModel + + +# 데이터의 basemodel + +class Item_schema(BaseModel): + symbol: str + position_type: str + + enter_time : datetime.datetime # 진입 시각 + close_time : datetime.datetime # 청산 시각 + + entry_price : float + purchase_price : float + eval_price : float + eval_PAL : float + revenue_rate : float + amount : float + + profit_end : float + loss_end : float + + onwer_id : int + + + class Config: + from_attributes = True \ No newline at end of file diff --git a/backend_copy/home.py b/backend_copy/home.py new file mode 100644 index 0000000..bcfdc20 --- /dev/null +++ b/backend_copy/home.py @@ -0,0 +1,101 @@ +from fastapi import FastAPI, Request, Form, BackgroundTasks, HTTPException +from fastapi.responses import HTMLResponse, JSONResponse +from fastapi.middleware.cors import CORSMiddleware +from strategys.init import binance +from strategys.position import enter_position, exit_position +from strategys.function import get_cur_price, get_balance, dataFrame, calAmount, VolatilityBreakout, get_symbol_info, dataFrame_day +from strategys.env import profit_percent, loss_percent, purchase_percent, con_diffma40_4, timeframe, symbols, k +from strategys.strategy import strategy_1, strategy_2, strategy_3 + + +from pydantic import BaseModel +from typing import Dict +import datetime +import concurrent.futures + +from domain.history import history_router +app = FastAPI() + + +# Add CORS middleware +origins = [ + "http://localhost", + "http://127.0.0.1:8000", + "http://127.0.0.1:5173", + "http://localhost:5173", + "http://127.0.0.1:5500" +] + +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +class Enter_info(BaseModel): + strategy : str + symbol : str + purchase_percent : float + leverage : int + # create_date: datetime.datetime + + +@app.get("/") +async def init(request: Request): + return {"message":"home"} + + +def position(strategys, symbol, purchase_percent, leverage): + if strategys == "strategy_1": + strategy_1(symbol, purchase_percent, leverage) + elif strategys == "strategy_2": + strategy_2(symbol, purchase_percent, leverage) + elif strategys == "strategy_3": + strategy_3(symbol, purchase_percent, leverage) + +# 실제로 포지션에 진입하기 위한 post +@app.post("/enter_position", response_model=Enter_info) +async def enter_position(enter_info: Enter_info, background_tasks:BackgroundTasks): # enter_info의 양식대로 데이터가 들어옴 + background_tasks.add_task(position, + enter_info.strategy, + enter_info.symbol, + enter_info.purchase_percent, + enter_info.leverage) + return enter_info + +# symbols_info를 return 함 +@app.get("/symbols_info", response_class=JSONResponse) +async def symbols_info(): + symbols = ["ETH/USDT", "BTC/USDT", "XRP/USDT", "DOGE/USDT", "SOL/USDT", "STX/USDT"] + + with concurrent.futures.ThreadPoolExecutor() as executor: + # 병렬로 dataFrame_day 함수를 호출합니다. + dataframes = list(executor.map(dataFrame_day, symbols)) + + data = {} + for symbol, df in zip(symbols, dataframes): + cur_price, daily_per = get_symbol_info(symbol, df) + data[f"{symbol.split('/')[0]}_cur_price"] = cur_price + data[f"{symbol.split('/')[0]}_daily_per"] = daily_per + + return JSONResponse(content=data) + +# @app.get("/history", response_class=HTMLResponse) +# def history(request: Request): +# return templates.TemplateResponse("history.html", {"request": request}) + +# @app.get("/mypage", response_class=HTMLResponse) +# def mypage(request: Request): +# return templates.TemplateResponse("mypage.html", {"request": request}) + +# @app.get("/setting", response_class=HTMLResponse) +# def mypage(request: Request): +# return templates.TemplateResponse("setting.html", {"request": request}) + + + + +app.include_router(history_router.router) \ No newline at end of file diff --git a/backend_copy/migrations/README b/backend_copy/migrations/README new file mode 100644 index 0000000..98e4f9c --- /dev/null +++ b/backend_copy/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/backend_copy/migrations/__pycache__/env.cpython-38.pyc b/backend_copy/migrations/__pycache__/env.cpython-38.pyc new file mode 100644 index 0000000..08920a5 Binary files /dev/null and b/backend_copy/migrations/__pycache__/env.cpython-38.pyc differ diff --git a/backend_copy/migrations/env.py b/backend_copy/migrations/env.py new file mode 100644 index 0000000..af1c5b4 --- /dev/null +++ b/backend_copy/migrations/env.py @@ -0,0 +1,79 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +import models +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = models.Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/backend_copy/migrations/script.py.mako b/backend_copy/migrations/script.py.mako new file mode 100644 index 0000000..55df286 --- /dev/null +++ b/backend_copy/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} diff --git a/backend_copy/migrations/versions/6f94d27eb622_.py b/backend_copy/migrations/versions/6f94d27eb622_.py new file mode 100644 index 0000000..3f40d0e --- /dev/null +++ b/backend_copy/migrations/versions/6f94d27eb622_.py @@ -0,0 +1,56 @@ +"""empty message + +Revision ID: 6f94d27eb622 +Revises: +Create Date: 2024-07-14 17:34:31.678724 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '6f94d27eb622' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('user', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_name', sa.String(), nullable=False), + sa.Column('password', sa.Text(), nullable=False), + sa.Column('balance', sa.Float(), nullable=False), + sa.Column('api_key', sa.Text(), nullable=False), + sa.Column('api_secret', sa.Text(), nullable=False), + sa.Column('create_date', sa.DateTime(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('item', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('symbol', sa.String(), nullable=False), + sa.Column('position_type', sa.Boolean(), nullable=False), + sa.Column('enter_time', sa.DateTime(), nullable=False), + sa.Column('close_time', sa.DateTime(), nullable=False), + sa.Column('entry_price', sa.Float(), nullable=False), + sa.Column('purchase_percent', sa.Float(), nullable=False), + sa.Column('eval_price', sa.Float(), nullable=False), + sa.Column('eval_PAL', sa.Float(), nullable=False), + sa.Column('revenue_rate', sa.Float(), nullable=False), + sa.Column('amount', sa.Float(), nullable=False), + sa.Column('profit_end', sa.Float(), nullable=False), + sa.Column('loss_end', sa.Float(), nullable=False), + sa.Column('onwer_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['onwer_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('item') + op.drop_table('user') + # ### end Alembic commands ### diff --git a/backend_copy/migrations/versions/88828f02f9ff_rename_purchase_percent_to_purchase_.py b/backend_copy/migrations/versions/88828f02f9ff_rename_purchase_percent_to_purchase_.py new file mode 100644 index 0000000..6e6561e --- /dev/null +++ b/backend_copy/migrations/versions/88828f02f9ff_rename_purchase_percent_to_purchase_.py @@ -0,0 +1,30 @@ +"""Rename purchase_percent to purchase_price + +Revision ID: 88828f02f9ff +Revises: b14b26de5503 +Create Date: 2024-07-15 16:17:11.458602 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '88828f02f9ff' +down_revision = 'b14b26de5503' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('item', 'purchase_percent', new_column_name='purchase_price') + + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('item', 'purchase_price', new_column_name='purchase_percent') + + # ### end Alembic commands ### diff --git a/backend_copy/migrations/versions/8d2723fff816_add_a_cur_price.py b/backend_copy/migrations/versions/8d2723fff816_add_a_cur_price.py new file mode 100644 index 0000000..9bfd6f5 --- /dev/null +++ b/backend_copy/migrations/versions/8d2723fff816_add_a_cur_price.py @@ -0,0 +1,25 @@ +"""add a cur_price + +Revision ID: 8d2723fff816 +Revises: c34811630b7f +Create Date: 2024-07-15 21:45:02.973218 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '8d2723fff816' +down_revision = 'c34811630b7f' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('item', sa.Column('cur_price', sa.Float)) + + + +def downgrade() -> None: + pass diff --git a/backend_copy/migrations/versions/__pycache__/6f94d27eb622_.cpython-38.pyc b/backend_copy/migrations/versions/__pycache__/6f94d27eb622_.cpython-38.pyc new file mode 100644 index 0000000..193198e Binary files /dev/null and b/backend_copy/migrations/versions/__pycache__/6f94d27eb622_.cpython-38.pyc differ diff --git a/backend_copy/migrations/versions/__pycache__/88828f02f9ff_rename_purchase_percent_to_purchase_.cpython-38.pyc b/backend_copy/migrations/versions/__pycache__/88828f02f9ff_rename_purchase_percent_to_purchase_.cpython-38.pyc new file mode 100644 index 0000000..90ec8d8 Binary files /dev/null and b/backend_copy/migrations/versions/__pycache__/88828f02f9ff_rename_purchase_percent_to_purchase_.cpython-38.pyc differ diff --git a/backend_copy/migrations/versions/__pycache__/8d2723fff816_add_a_cur_price.cpython-38.pyc b/backend_copy/migrations/versions/__pycache__/8d2723fff816_add_a_cur_price.cpython-38.pyc new file mode 100644 index 0000000..190e0a1 Binary files /dev/null and b/backend_copy/migrations/versions/__pycache__/8d2723fff816_add_a_cur_price.cpython-38.pyc differ diff --git a/backend_copy/migrations/versions/__pycache__/b14b26de5503_.cpython-38.pyc b/backend_copy/migrations/versions/__pycache__/b14b26de5503_.cpython-38.pyc new file mode 100644 index 0000000..313fe4d Binary files /dev/null and b/backend_copy/migrations/versions/__pycache__/b14b26de5503_.cpython-38.pyc differ diff --git a/backend_copy/migrations/versions/__pycache__/c34811630b7f_.cpython-38.pyc b/backend_copy/migrations/versions/__pycache__/c34811630b7f_.cpython-38.pyc new file mode 100644 index 0000000..0d62be5 Binary files /dev/null and b/backend_copy/migrations/versions/__pycache__/c34811630b7f_.cpython-38.pyc differ diff --git a/backend_copy/migrations/versions/__pycache__/dc8d8be5e9ac_remove_cur_price.cpython-38.pyc b/backend_copy/migrations/versions/__pycache__/dc8d8be5e9ac_remove_cur_price.cpython-38.pyc new file mode 100644 index 0000000..1ffcdf3 Binary files /dev/null and b/backend_copy/migrations/versions/__pycache__/dc8d8be5e9ac_remove_cur_price.cpython-38.pyc differ diff --git a/backend_copy/migrations/versions/b14b26de5503_.py b/backend_copy/migrations/versions/b14b26de5503_.py new file mode 100644 index 0000000..e3b9918 --- /dev/null +++ b/backend_copy/migrations/versions/b14b26de5503_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: b14b26de5503 +Revises: 6f94d27eb622 +Create Date: 2024-07-15 16:13:48.352986 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'b14b26de5503' +down_revision = '6f94d27eb622' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/backend_copy/migrations/versions/c34811630b7f_.py b/backend_copy/migrations/versions/c34811630b7f_.py new file mode 100644 index 0000000..c2095af --- /dev/null +++ b/backend_copy/migrations/versions/c34811630b7f_.py @@ -0,0 +1,28 @@ +"""empty message + +Revision ID: c34811630b7f +Revises: 88828f02f9ff +Create Date: 2024-07-15 21:42:03.854996 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'c34811630b7f' +down_revision = '88828f02f9ff' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/backend_copy/migrations/versions/dc8d8be5e9ac_remove_cur_price.py b/backend_copy/migrations/versions/dc8d8be5e9ac_remove_cur_price.py new file mode 100644 index 0000000..1f9b4ad --- /dev/null +++ b/backend_copy/migrations/versions/dc8d8be5e9ac_remove_cur_price.py @@ -0,0 +1,23 @@ +"""remove cur_price + +Revision ID: dc8d8be5e9ac +Revises: 8d2723fff816 +Create Date: 2024-07-15 21:52:35.980758 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'dc8d8be5e9ac' +down_revision = '8d2723fff816' +branch_labels = None +depends_on = None + + +def upgrade(): + op.drop_column("item", "cur_price") + +def downgrade() -> None: + pass diff --git a/backend_copy/models.py b/backend_copy/models.py new file mode 100644 index 0000000..46f6209 --- /dev/null +++ b/backend_copy/models.py @@ -0,0 +1,40 @@ +from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Float +from sqlalchemy.orm import relationship +from database import Base + + +# db table에 어떤 형식으로 데이터를 넣을지 + +class User(Base): + __tablename__ = "user" + + id = Column(Integer, primary_key=True) + user_name = Column(String, nullable=False) + password = Column(Text, nullable=False) + balance = Column(Float, nullable=False) + api_key = Column(Text, nullable=False) + api_secret = Column(Text, nullable=False) + create_date = Column(DateTime, nullable=False) + +class Item(Base): + __tablename__ = "item" + + id = Column(Integer, primary_key=True) + symbol = Column(String, nullable=False) # 코인 + position_type = Column(String, nullable=False) + + enter_time = Column(DateTime, nullable=False) # 진입 시각 + close_time = Column(DateTime, nullable=False) # 청산 시각 + + entry_price = Column(Float, nullable=False) + purchase_price = Column(Float, nullable=False) + eval_price = Column(Float, nullable=False) + eval_PAL = Column(Float, nullable=False) + revenue_rate = Column(Float, nullable=False) + amount = Column(Float, nullable=False) + + profit_end = Column(Float, nullable=False) + loss_end = Column(Float, nullable=False) + + onwer_id = Column(Integer, ForeignKey("user.id")) + onwer = relationship("User", backref="item") diff --git a/backend_copy/strategys/__pycache__/env.cpython-38.pyc b/backend_copy/strategys/__pycache__/env.cpython-38.pyc new file mode 100644 index 0000000..63c486c Binary files /dev/null and b/backend_copy/strategys/__pycache__/env.cpython-38.pyc differ diff --git a/backend_copy/strategys/__pycache__/function.cpython-38.pyc b/backend_copy/strategys/__pycache__/function.cpython-38.pyc new file mode 100644 index 0000000..21c616a Binary files /dev/null and b/backend_copy/strategys/__pycache__/function.cpython-38.pyc differ diff --git a/backend_copy/strategys/__pycache__/init.cpython-38.pyc b/backend_copy/strategys/__pycache__/init.cpython-38.pyc new file mode 100644 index 0000000..86b090e Binary files /dev/null and b/backend_copy/strategys/__pycache__/init.cpython-38.pyc differ diff --git a/backend_copy/strategys/__pycache__/position.cpython-38.pyc b/backend_copy/strategys/__pycache__/position.cpython-38.pyc new file mode 100644 index 0000000..693a585 Binary files /dev/null and b/backend_copy/strategys/__pycache__/position.cpython-38.pyc differ diff --git a/backend_copy/strategys/__pycache__/strategy.cpython-38.pyc b/backend_copy/strategys/__pycache__/strategy.cpython-38.pyc new file mode 100644 index 0000000..fda5f1d Binary files /dev/null and b/backend_copy/strategys/__pycache__/strategy.cpython-38.pyc differ diff --git a/backend_copy/strategys/env.py b/backend_copy/strategys/env.py new file mode 100644 index 0000000..b2b8b85 --- /dev/null +++ b/backend_copy/strategys/env.py @@ -0,0 +1,19 @@ +########################################################## + +##### 수수료 #### +# 지정가 : 0.02% * 2 +# 시장가 : 0.04% * 2 +################ +profit_percent = 0.02# 0.8% +loss_percent = 0.04 # 1.6% +purchase_percent = 0.5 # 50% +con_diffma40_4 = 10 # 횡보 조건 +timeframe = "1d" +symbols = "ETH/USDT" # 이더리움 + +k = 0.5 # VB K 값 +################################ + +########################################################### + + \ No newline at end of file diff --git a/backend_copy/strategys/function.py b/backend_copy/strategys/function.py new file mode 100644 index 0000000..c8523cf --- /dev/null +++ b/backend_copy/strategys/function.py @@ -0,0 +1,73 @@ +from .init import binance +from .env import k, timeframe +import pandas as pd +import math +# O,H,L,C,V 조회 +def dataFrame(symbol): + # 데이터프레임 객체로 변환 + OHLCV = binance.fetch_ohlcv(symbol, timeframe=timeframe, limit=5000) + df = pd.DataFrame(OHLCV, + columns = ["datetime", "open", "high", "low", "close", "volume"]) + df["datetime"] = pd.to_datetime(df["datetime"], unit="ms") + pd.Timedelta(hours=9) + + df.set_index("datetime", inplace=True) + + # sma 설정 : ema는 trading view의 계산값이랑 오차가 있음 + df["sma4"] = df["close"].rolling(10).mean() + df["sma8"] = df["close"].rolling(30).mean() + df["sma30"] = df["close"].rolling(80).mean() + df["sma40"] = df["close"].rolling(120).mean() + + df["diffMa40_4"] = abs(df["sma40"] - df["sma4"]) + + return df + +def dataFrame_day(symbol): + # 데이터프레임 객체로 변환 + OHLCV = binance.fetch_ohlcv(symbol, timeframe="1d", limit=5000) + df = pd.DataFrame(OHLCV, + columns = ["datetime", "open", "high", "low", "close", "volume"]) + df["datetime"] = pd.to_datetime(df["datetime"], unit="ms") + pd.Timedelta(hours=9) + return df +# 코인 구매 수량 계산 +def calAmount(balance, cur_price, purchase_percent = 0.1): + Trade = balance * purchase_percent # 전체 자본의 portion : 10% + MTA = 100000 # Minimum Trade Amount : 해당 숫자는 코인마다 상이하므로 수정해야함 0.00001 + amount = math.floor((Trade * MTA)/cur_price) / MTA + return amount + +# 잔고 조회 +def get_balance(): + # 잔고 조회 + balance = binance.fetch_balance() + # USDT 잔고 조회 + USDTBalance = balance["total"]["USDT"] + return USDTBalance + +# 현재가 조회 +def get_cur_price(symbol): + # 현재가 + coin = binance.fetch_ticker(symbol) + cur_price = coin["last"] + return cur_price + +############################################################# + +# 변동성 돌판전략 +def VolatilityBreakout(df): + preCandle = df.iloc[-2] + curcandle = df.iloc[-1] + preCandleHigh = preCandle["high"] # 전 봉의 고가 + preCandleLow = preCandle["low"] # 전 봉의 저가 + curCandleOpen = curcandle["open"] # 현재 봉의 시가 + target = (preCandleHigh - preCandleLow) * k # 고가 - 저가 + longTarget = curCandleOpen + target # 롱 타겟 + shortTarget = curCandleOpen - target # 숏 타겟 + return longTarget, shortTarget + + +def get_symbol_info(symbol, df): + cur_price = get_cur_price(symbol) + daily_percent = round(((cur_price - df.iloc[-1]["open"]) / df.iloc[-1]["open"]) * 100, 2) # 9시를 시작가격으로 봄 + return cur_price, daily_percent + diff --git a/backend_copy/strategys/init.py b/backend_copy/strategys/init.py new file mode 100644 index 0000000..e1bc362 --- /dev/null +++ b/backend_copy/strategys/init.py @@ -0,0 +1,14 @@ +import ccxt + +with open("../apiKey.txt") as f: + lines = f.readlines() + apiKey = lines[0].strip() + secret = lines[1].strip() + +binance = ccxt.binance(config={ + "apiKey": apiKey, + "secret":secret, + "enableRateLimit":True, + "options":{"defaultType":"future"} # 선물거래 명시 +}) + diff --git a/backend_copy/strategys/position.py b/backend_copy/strategys/position.py new file mode 100644 index 0000000..11752ee --- /dev/null +++ b/backend_copy/strategys/position.py @@ -0,0 +1,24 @@ +def enter_position(exchange, symbol, cur_price, amount, target, position): + if target == 1: + position["type"] = "long" + position["amount"] = amount + # exchange.create_market_buy_order(symbol=symbol, amount=amount) + print(":::::::enter long") + elif target == -1: + position["type"] = "short" + position["amount"] = amount + # exchange.create_market_sell_order(symbol=symbol, amount=amount) + print(":::::::enter short") + else: + print(":::::::매수 X") + +# 포지션 종료 +def exit_position(exchange, symbol, cur_price, amount, position): + if position["type"] =="long": + # exchange.create_market_sell_order(symbol=symbol, amount=amount) + print(":::::::exit long") + position["type"] = None + elif position["type"] == "short": + # exchange.create_market_buy_order(symbol=symbol, amount=amount) + print(":::::::exit short") + position["type"] = None diff --git a/backend_copy/strategys/strategy.py b/backend_copy/strategys/strategy.py new file mode 100644 index 0000000..ddf20d4 --- /dev/null +++ b/backend_copy/strategys/strategy.py @@ -0,0 +1,239 @@ +import time +from .init import binance +from .position import enter_position, exit_position +from .function import get_cur_price, get_balance, dataFrame, calAmount, VolatilityBreakout +from .env import profit_percent, loss_percent, purchase_percent, con_diffma40_4, timeframe, symbols, k +from domain.history.history_function import create_history, update_history # if 문 사이에 넣기 +import datetime + +def print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price): + print("=================================ETH/USDT=============================================") + print("----BASE----") + print(f"sma4 : {df['sma4'].iloc[-1]}, sma30 : {df['sma30'].iloc[-1]}") + print(f"long => {df['sma4'].iloc[-1]> df['sma30'].iloc[-1]} short => {df['sma4'].iloc[-1] < df['sma30'].iloc[-1]}") + print("----VB----") + print(f"long_target : {long_target} /// cur_price {cur_price} => {long_target < cur_price}") + print(f"short_target : {short_target} /// cur_price : {cur_price} => {short_target > cur_price}") + print("----position----") + print(f"position : {position['type']}") + print("----횡보조건----") + print(f"횡보 조건 : {(diffma40_4 > con_diffma40_4)}") + print(f"entry_price : {entry_price}") + + print("-----------------청산 조건-----------------") + print(f"long profit end : {entry_price + entry_price * profit_percent} /// {cur_price} => {entry_price + entry_price * profit_percent < cur_price}") + print(f"long loss end : {entry_price - entry_price * loss_percent} /// {cur_price} => {entry_price - entry_price * loss_percent > cur_price}") + + print(f"short profit end : {entry_price - entry_price * profit_percent} /// {cur_price} => {entry_price - entry_price * profit_percent > cur_price}") + print(f"short loss end : {entry_price + entry_price * loss_percent} /// {cur_price} => {entry_price + entry_price * loss_percent < cur_price}") + +def set_leverage(symbol, leverage=1): + # leverage 설정 + markets = binance.load_markets() + market = binance.market(symbol) + resp = binance.fapiprivate_post_leverage({ + "symbol":market["id"], + "leverage":leverage + }) + + +def strategy_1(symbol = symbols, purchase_percent=purchase_percent, leverage=1): + + entry_price = 0 + position = { + "type":None, + "amount":0 + } + + set_leverage(symbol, leverage) + while(True): + # 현재가 + cur_price = get_cur_price(symbol) + + # 잔고 + balance = get_balance() + # dataFrame + df = dataFrame(symbol) + + # 구매 수량 + amount = calAmount(balance, cur_price, purchase_percent) # 50% + + # 변동성 돌파전략 + long_target, short_target = VolatilityBreakout(dataFrame(symbol)) + + # sma 설정 + sma4 = df["sma4"].iloc[-1] + sma8 = df["sma8"].iloc[-1] + sma30 = df["sma30"].iloc[-1] + sma40 = df["sma40"].iloc[-1] + diffma40_4 = df["diffMa40_4"].iloc[-1] + + # long position 진입 조건 + is_long = ((sma4 > sma30) & (cur_price > long_target) & (diffma40_4 > con_diffma40_4) & (position["type"] == None)) + # short position 진입 조건 + is_short = ((sma4 < sma30) & (cur_price < short_target) & (diffma40_4 > con_diffma40_4) & (position["type"] == None)) + + # long position 청산 조건 + is_long_end = (((entry_price + entry_price * profit_percent < cur_price) and position["type"] == "long") or ((entry_price - entry_price * loss_percent > cur_price) and position["type"] == "long") ) + # short position 청산 조건 + is_short_end = (((entry_price - entry_price * profit_percent > cur_price) and position["type"] == "short") or ((entry_price + entry_price * loss_percent < cur_price) and position["type"] =="short")) + # print(df) + # position 진입 + if is_long: + entry_price = cur_price + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + enter_position(binance, symbol=symbol, cur_price = cur_price, amount=amount, target=1, position=position) + + if is_long_end: + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + exit_position(binance, symbol=symbol, cur_price=cur_price, amount = position["amount"], position=position) + + + if is_short: + entry_price = cur_price + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + enter_position(binance, symbol=symbol, cur_price = cur_price, amount=amount, target=-1, position=position) + + if is_short_end: + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + exit_position(binance, symbol=symbol, cur_price=cur_price, amount = position["amount"], position=position) + + else: + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + + + + # histroy의 상단 업데이트 + + time.sleep(1) + +def strategy_2(symbol = symbols, purchase_percent=purchase_percent, leverage=1): + # while(True): + entry_price = 0 + position = { + "type":None, + "amount":0 + } + set_leverage(symbol, leverage) + for _ in range(5): + # 현재가 + cur_price = get_cur_price(symbol) + + # 잔고 + balance = get_balance() + + # dataFrame + df = dataFrame(symbol) + + # 구매 수량 + amount = calAmount(balance, cur_price, purchase_percent) # 50% + + # 변동성 돌파전략 + long_target, short_target = VolatilityBreakout(dataFrame(symbol)) + + # sma 설정 + sma4 = df["sma4"].iloc[-1] + sma8 = df["sma8"].iloc[-1] + sma30 = df["sma30"].iloc[-1] + sma40 = df["sma40"].iloc[-1] + diffma40_4 = df["diffMa40_4"].iloc[-1] + + # long position 진입 조건 + is_long = ((sma4 > sma30) & (cur_price > long_target) & (diffma40_4 > con_diffma40_4) & (position["type"] == None)) + # short position 진입 조건 + is_short = ((sma4 < sma30) & (cur_price < short_target) & (diffma40_4 > con_diffma40_4) & (position["type"] == None)) + + # long position 청산 조건 + is_long_end = (((entry_price + entry_price * profit_percent < cur_price) and position["type"] == "long") or ((entry_price - entry_price * loss_percent > cur_price) and position["type"] == "long") ) + # short position 청산 조건 + is_short_end = (((entry_price - entry_price * profit_percent > cur_price) and position["type"] == "short") or ((entry_price + entry_price * loss_percent < cur_price) and position["type"] =="short")) + # print(df) + # position 진입 + if is_long: + entry_price = cur_price + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + enter_position(binance, symbol=symbol, cur_price = cur_price, amount=amount, target=1, position=position) + + if is_long_end: + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + exit_position(binance, symbol=symbol, cur_price=cur_price, amount = position["amount"], position=position) + + + if is_short: + entry_price = cur_price + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + enter_position(binance, symbol=symbol, cur_price = cur_price, amount=amount, target=-1, position=position) + + if is_short_end: + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + exit_position(binance, symbol=symbol, cur_price=cur_price, amount = position["amount"], position=position) + + else: + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + time.sleep(1) + + +def strategy_3(symbol = symbols, purchase_percent=purchase_percent, leverage=1): + # while(True): + entry_price = 0 + position = { + "type":None, + "amount":0 + } + set_leverage(symbol, leverage) + for _ in range(5): + # 현재가 + cur_price = get_cur_price(symbol) + + # 잔고 + balance = get_balance() + + # dataFrame + df = dataFrame(symbol) + + # 구매 수량 + amount = calAmount(balance, cur_price, purchase_percent) # 50% + + # 변동성 돌파전략 + long_target, short_target = VolatilityBreakout(dataFrame(symbol)) + + # sma 설정 + sma4 = df["sma4"].iloc[-1] + sma8 = df["sma8"].iloc[-1] + sma30 = df["sma30"].iloc[-1] + sma40 = df["sma40"].iloc[-1] + diffma40_4 = df["diffMa40_4"].iloc[-1] + + # long position 진입 조건 + is_long = ((sma4 > sma30) & (cur_price > long_target) & (diffma40_4 > con_diffma40_4) & (position["type"] == None)) + # short position 진입 조건 + is_short = ((sma4 < sma30) & (cur_price < short_target) & (diffma40_4 > con_diffma40_4) & (position["type"] == None)) + + # long position 청산 조건 + is_long_end = (((entry_price + entry_price * profit_percent < cur_price) and position["type"] == "long") or ((entry_price - entry_price * loss_percent > cur_price) and position["type"] == "long") ) + # short position 청산 조건 + is_short_end = (((entry_price - entry_price * profit_percent > cur_price) and position["type"] == "short") or ((entry_price + entry_price * loss_percent < cur_price) and position["type"] =="short")) + # print(df) + # position 진입 + if is_long: + entry_price = cur_price + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + enter_position(binance, symbol=symbol, cur_price = cur_price, amount=amount, target=1, position=position) + + if is_long_end: + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + exit_position(binance, symbol=symbol, cur_price=cur_price, amount = position["amount"], position=position) + + + if is_short: + entry_price = cur_price + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + enter_position(binance, symbol=symbol, cur_price = cur_price, amount=amount, target=-1, position=position) + + if is_short_end: + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + exit_position(binance, symbol=symbol, cur_price=cur_price, amount = position["amount"], position=position) + + else: + print_info(df, long_target, short_target, cur_price, diffma40_4, position, entry_price) + time.sleep(1) diff --git a/backend_copy/templates/goto.html b/backend_copy/templates/goto.html new file mode 100644 index 0000000..8c1a9f4 --- /dev/null +++ b/backend_copy/templates/goto.html @@ -0,0 +1,51 @@ + + + + 코인 전략 선택 + + + + + + +
+ Bit4Coin + +
+
+
+
+ + + +
Binance
+
+
+ + + +
TradingView
+
+
+ +
History
+
+
+ + + +
GitHub
+
+
+
+ + + diff --git a/backend_copy/templates/history.html b/backend_copy/templates/history.html new file mode 100644 index 0000000..70cf394 --- /dev/null +++ b/backend_copy/templates/history.html @@ -0,0 +1,30 @@ + + + + 코인 전략 선택 + + + + + +
+ Bit4Coin + +
+
+ + +
+ + + + + + + diff --git a/backend_copy/templates/home.html b/backend_copy/templates/home.html new file mode 100644 index 0000000..3b66988 --- /dev/null +++ b/backend_copy/templates/home.html @@ -0,0 +1,75 @@ + + + + 코인 전략 선택 + + + + + + +
+ Bit4Coin + +
+
+
+
+
ETH
+
+ +
+
+ +
+
+
+ + + + + + +
+ +
+
+ + + +
+
+
+
+
+
+
+ + + + + + diff --git a/backend_copy/templates/index.html b/backend_copy/templates/index.html new file mode 100644 index 0000000..3383ad6 --- /dev/null +++ b/backend_copy/templates/index.html @@ -0,0 +1,58 @@ + + + + 코인 전략 선택 + + + + +
+

Bit4Coin

+
+
+ + +
+
+ + + + + + +
+
+ +
+
+ + +

+ + +

+ + +

+ + + + +
+
+
+
+ {% if result %} +

{{ coinName }}

+ {% if strategy == "balance" %} + balance : {{ result }} + {% else %} + cur_price : {{ result }} + {% endif %} + postion : {{ target }} + {% endif %} + + diff --git a/backend_copy/templates/main.html b/backend_copy/templates/main.html new file mode 100644 index 0000000..7d78fda --- /dev/null +++ b/backend_copy/templates/main.html @@ -0,0 +1,55 @@ + + + + 코인 전략 선택 + + + + + +
+

Bit4Coin

+
+
+
+ +
+
+ +
+
+ +
+ + + +
+
+
+ + + +
+
+
+ your strategy is {{result}} +
+ +
+ + + + + + + + +
+
+
+ + diff --git a/backend_copy/templates/mypage.html b/backend_copy/templates/mypage.html new file mode 100644 index 0000000..3608c68 --- /dev/null +++ b/backend_copy/templates/mypage.html @@ -0,0 +1,30 @@ + + + + 코인 전략 선택 + + + + + +
+ Bit4Coin + +
+
+ + +
+ + + + + + + diff --git a/backend_copy/templates/setting.html b/backend_copy/templates/setting.html new file mode 100644 index 0000000..b192667 --- /dev/null +++ b/backend_copy/templates/setting.html @@ -0,0 +1,30 @@ + + + + 코인 전략 선택 + + + + + +
+ Bit4Coin + +
+
+ + +
+ + + + + + + diff --git a/backend_copy/templates/static/goto.css b/backend_copy/templates/static/goto.css new file mode 100644 index 0000000..7ecb70e --- /dev/null +++ b/backend_copy/templates/static/goto.css @@ -0,0 +1,49 @@ +body { + width: 360px; + height: 772px; +} +header { + border-bottom: 3px solid black; + text-align: center; + font-size: 35px; + padding-left: 40px; + padding-bottom: 5px; +} +#img_setting { + float: right; + width: 35px; + height: 35px; + margin-right: 5px; + margin-top: 5px; +} +main { + height: 605px; +} +.container { + padding-top: 100px; + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: repeat(2, 1fr); + gap: 10px; /* 요소 사이의 간격을 추가할 수 있습니다 */ + justify-items: center; + align-items: center; +} +.item { + text-align: center; + margin-bottom: 100px; +} +.item img { + width: 100px; + height: 100px; +} +footer { + display: flex; + border-top: 3px solid black; + text-align: center; + justify-content: space-evenly; + padding-top: 5px; +} +footer img { + width: 40px; + height: 40px; +} \ No newline at end of file diff --git a/backend_copy/templates/static/goto.js b/backend_copy/templates/static/goto.js new file mode 100644 index 0000000..7783ec0 --- /dev/null +++ b/backend_copy/templates/static/goto.js @@ -0,0 +1,4 @@ +function goTo(url) { + const encoded = encodeURIComponent(url); + window.location.href = `/backend_copy/templates/${encoded}.html`; +} \ No newline at end of file diff --git a/backend_copy/templates/static/history.css b/backend_copy/templates/static/history.css new file mode 100644 index 0000000..85800c5 --- /dev/null +++ b/backend_copy/templates/static/history.css @@ -0,0 +1,39 @@ +body { + width: 360px; + height: 772px; +} +header { + border-bottom: 3px solid black; + text-align: center; + font-size: 35px; + padding-left: 40px; + padding-bottom: 5px; +} + +#img_setting { + float: right; + width: 35px; + height: 35px; + margin-right: 5px; + margin-top: 5px; +} +main { + height: 605px; +} +img { + width: 50px; + height: 50px; +} + +footer { + display: flex; + border-top: 3px solid black; + text-align: center; + justify-content: space-evenly; + padding-top: 5px; +} + +footer img { + width: 40px; + height: 40px; +} diff --git a/backend_copy/templates/static/history.js b/backend_copy/templates/static/history.js new file mode 100644 index 0000000..7783ec0 --- /dev/null +++ b/backend_copy/templates/static/history.js @@ -0,0 +1,4 @@ +function goTo(url) { + const encoded = encodeURIComponent(url); + window.location.href = `/backend_copy/templates/${encoded}.html`; +} \ No newline at end of file diff --git a/backend_copy/templates/static/home.css b/backend_copy/templates/static/home.css new file mode 100644 index 0000000..50a0e0e --- /dev/null +++ b/backend_copy/templates/static/home.css @@ -0,0 +1,138 @@ +body { + width: 360px; + height: 772px; +} +header { + border-bottom: 3px solid black; + text-align: center; + font-size: 35px; + padding-left: 40px; + padding-bottom: 5px; +} +#img_setting { + float: right; + width: 35px; + height: 35px; + margin-right: 5px; + margin-top: 5px; +} +main { + height: 605px; +} +#symbol_name { + text-align: center; + font-size: 25px; +} +#graph { + text-align: center; +} +#img_graph { + width: 280px; + height: 180px; +} + +.container { + width: 300px; + display: inline-flex; + justify-content: space-around; + margin-top: 30px; +} +#section_input{ + width: 30%; + margin-left: 15px; +} + +#section_input .input_info { + width: 130px; + height: 30px; + border-radius: 10px; + margin-bottom: 15px; +} + +#section_input select { + width: 130px; + height: 30px; +} + +#section_desc { + width: 30%; + margin-left: 30px; +} +#enter { + width: 300px; + height: 30px; + margin-top: 80px; +} +#description { + border: 1px solid gray; + width: 130px; + height: 210px; + font-size: 10px; + text-align: center; +} + +#enter:hover { + box-shadow: 3px 4px 11px 0px #00000040; +} + +/*모달 팝업 영역 스타일링*/ +.modal_detail { +/*팝업 배경*/ + display: none; /*평소에는 보이지 않도록*/ + position: absolute; + top:0; + left: 0; + width: 100%; + height: 100vh; + overflow: hidden; + background: rgba(0,0,0,0.5); +} + +.modal_detail #detail_popup { +/*팝업*/ + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding: 20px; + background: #ffffff; + border-radius: 20px; +} +.modal_btn_container { + display: flex; + flex-direction: row; + justify-content: space-around; +} +.modal_detail #detail_popup #close_detail { + display: block; + padding: 10px 20px; + background-color: rgb(116, 0, 0); + border: none; + border-radius: 5px; + color: #fff; + cursor: pointer; + transition: box-shadow 0.2s; +} +.modal_detail #detail_popup #enter_position { + display: block; + padding: 10px 20px; + background-color: rgb(204, 255, 0); + border: none; + border-radius: 5px; + color: #fff; + cursor: pointer; + transition: box-shadow 0.2s; +} + +footer { + display: flex; + border-top: 3px solid black; + text-align: center; + justify-content: space-evenly; + padding-top: 5px; +} + +footer img { + width: 40px; + height: 40px; +} \ No newline at end of file diff --git a/backend_copy/templates/static/home.js b/backend_copy/templates/static/home.js new file mode 100644 index 0000000..e05ff47 --- /dev/null +++ b/backend_copy/templates/static/home.js @@ -0,0 +1,106 @@ + +function goTo(url) { + const encoded = encodeURIComponent(url); + window.location.href = `/backend_copy/templates/${encoded}.html`; +} + +document.addEventListener("DOMContentLoaded", function() { + // 전략선택시 간단하게 설명해주는 desc + function update_description() { + const symbol = document.getElementById('symbol').value; + const purchase_percent = document.getElementById('purchase_percent').value; + const leverage = document.getElementById('leverage').value; + const strategy = document.getElementById('strategy').value; + + let strategy_desc = ''; + if (strategy === 'strategy_1') { + strategy_desc = ` +

${symbol}

+

purchase percent : ${purchase_percent*100}%

+

leverage : ${leverage}배

+

profit percent : 0.8%

+

loss percent : 1.6%

+

timeframe : 15m

+

변동성 돌파전략과 SMA를 사용

+ `; + } else if (strategy === 'strategy_2') { + strategy_desc = ` +

${symbol}

+

purchase percent : ${purchase_percent*100}%

+

leverage : ${leverage}배

+

profit percent : 0.8%

+

loss percent : 1.6%

+

timeframe : 30m

+

변동성 돌파전략과 SMA를 사용

+ `; + } else if (strategy === 'strategy_3') { + strategy_desc = ` +

${symbol}

+

purchase percent : ${purchase_percent*100}%

+

leverage : ${leverage}배

+

profit percent : 2%

+

loss percent : 4%

+

timeframe : 1d

+

변동성 돌파전략과 SMA를 사용

+ `; + } + + // desciption의 내용 : brief + document.getElementById('description').innerHTML = strategy_desc; + + // modal의 내용 : detail + modal_desc = strategy_desc + "디테일한 내용 추가하기!!" + document.getElementById("modal_des").innerHTML = modal_desc; + } + + document.getElementById('strategy').addEventListener('change', update_description); +}); + + + +document.addEventListener("DOMContentLoaded", function() { + const modal = document.querySelector('.modal_detail'); + const modalOpen = document.querySelector('#enter'); + const modalClose = document.querySelector('#close_detail'); + + // Open modal when the button is clicked + modalOpen.addEventListener('click', function(){ + // Display modal + modal.style.display = 'block'; + }); + + // Close modal when the close button is clicked + modalClose.addEventListener('click', function(){ + // Hide modal + modal.style.display = 'none'; + }); + + // Submit form when the Yes button is clicked + const yesButton = document.querySelector('#enter_position'); + yesButton.addEventListener('click', function() { + const form = document.getElementById('enter_position_form'); + const formData = new FormData(form); + const jsonData = {}; + formData.forEach((value, key) => { + jsonData[key] = value; + }); + + fetch("http://127.0.0.1:8000/enter_position", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(jsonData), + }) + .then(response => response.json()) + .then(data => { + console.log(data); + + modal.style.display = 'none'; // Close modal after submission + }) + .catch(error => { + console.error('Error:', error); + }); + }); + +}); \ No newline at end of file diff --git a/backend_copy/templates/static/img/Binance_Logo.png b/backend_copy/templates/static/img/Binance_Logo.png new file mode 100644 index 0000000..c431a83 Binary files /dev/null and b/backend_copy/templates/static/img/Binance_Logo.png differ diff --git a/backend_copy/templates/static/img/arrow.png b/backend_copy/templates/static/img/arrow.png new file mode 100644 index 0000000..5c6d6c5 Binary files /dev/null and b/backend_copy/templates/static/img/arrow.png differ diff --git a/backend_copy/templates/static/img/arrow1.png b/backend_copy/templates/static/img/arrow1.png new file mode 100644 index 0000000..b917d50 Binary files /dev/null and b/backend_copy/templates/static/img/arrow1.png differ diff --git a/backend_copy/templates/static/img/github_logo.png b/backend_copy/templates/static/img/github_logo.png new file mode 100644 index 0000000..579a01d Binary files /dev/null and b/backend_copy/templates/static/img/github_logo.png differ diff --git a/backend_copy/templates/static/img/graph.jpg b/backend_copy/templates/static/img/graph.jpg new file mode 100644 index 0000000..65e3d3d Binary files /dev/null and b/backend_copy/templates/static/img/graph.jpg differ diff --git a/backend_copy/templates/static/img/history.png b/backend_copy/templates/static/img/history.png new file mode 100644 index 0000000..6006da4 Binary files /dev/null and b/backend_copy/templates/static/img/history.png differ diff --git a/backend_copy/templates/static/img/history1.png b/backend_copy/templates/static/img/history1.png new file mode 100644 index 0000000..a396cb5 Binary files /dev/null and b/backend_copy/templates/static/img/history1.png differ diff --git a/backend_copy/templates/static/img/home.png b/backend_copy/templates/static/img/home.png new file mode 100644 index 0000000..71e252f Binary files /dev/null and b/backend_copy/templates/static/img/home.png differ diff --git a/backend_copy/templates/static/img/home1.png b/backend_copy/templates/static/img/home1.png new file mode 100644 index 0000000..4169d76 Binary files /dev/null and b/backend_copy/templates/static/img/home1.png differ diff --git a/backend_copy/templates/static/img/mypage.png b/backend_copy/templates/static/img/mypage.png new file mode 100644 index 0000000..534fd55 Binary files /dev/null and b/backend_copy/templates/static/img/mypage.png differ diff --git a/backend_copy/templates/static/img/mypage1.png b/backend_copy/templates/static/img/mypage1.png new file mode 100644 index 0000000..b71361c Binary files /dev/null and b/backend_copy/templates/static/img/mypage1.png differ diff --git a/backend_copy/templates/static/img/setting.png b/backend_copy/templates/static/img/setting.png new file mode 100644 index 0000000..d99816f Binary files /dev/null and b/backend_copy/templates/static/img/setting.png differ diff --git a/backend_copy/templates/static/img/symbols.png b/backend_copy/templates/static/img/symbols.png new file mode 100644 index 0000000..813e05b Binary files /dev/null and b/backend_copy/templates/static/img/symbols.png differ diff --git a/backend_copy/templates/static/img/symbols1.png b/backend_copy/templates/static/img/symbols1.png new file mode 100644 index 0000000..274f6f4 Binary files /dev/null and b/backend_copy/templates/static/img/symbols1.png differ diff --git a/backend_copy/templates/static/img/tradingview_logo.png b/backend_copy/templates/static/img/tradingview_logo.png new file mode 100644 index 0000000..fbb2202 Binary files /dev/null and b/backend_copy/templates/static/img/tradingview_logo.png differ diff --git a/backend_copy/templates/static/market.css b/backend_copy/templates/static/market.css new file mode 100644 index 0000000..7fca5cc --- /dev/null +++ b/backend_copy/templates/static/market.css @@ -0,0 +1,76 @@ +body { + font-family: Arial, sans-serif; +} + + +.market-container { + display: flex; + flex-wrap: wrap; + gap: 10px; + padding: 20px; +} +.market-item { + background-color: #f2f2f2; + border: 2px solid #ccc; + border-radius: 10%; + width: 100px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + font-size: 14px; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); +} +.market-item:hover { + background-color: #bebebe; + border: 2px solid #ccc; + border-radius: 10%; + width: 100px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + font-size: 14px; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); +} + +.market-item:active { + background-color: #6e6e6e; + border: 2px solid #ccc; + border-radius: 10%; + width: 100px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + font-size: 14px; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); +} + +.search-container { + margin: 20px; +} +.search-input { + padding: 10px; + font-size: 16px; + width: 200px; + border: 1px solid #ccc; + border-radius: 5px; +} +.goto-main { + padding: 10px 20px; + font-size: 16px; + border: none; + border-radius: 5px; + background-color: #acabab; + color: white; + cursor: pointer; + text-decoration: none; + display: inline-block; +} +.goto-main:hover { + background-color: #5f5f5f; +} \ No newline at end of file diff --git a/backend_copy/templates/static/market.js b/backend_copy/templates/static/market.js new file mode 100644 index 0000000..920382b --- /dev/null +++ b/backend_copy/templates/static/market.js @@ -0,0 +1,18 @@ + + +function filterMarkets() { + const searchInput = document.getElementById('search').value.toLowerCase(); + const marketItems = document.getElementsByClassName('market-item'); + + for (let i = 0; i < marketItems.length; i++) { + const market = marketItems[i].textContent.toLowerCase(); + if (market.includes(searchInput)) { + marketItems[i].style.display = 'flex'; + } else { + marketItems[i].style.display = 'none'; + } + } +} +function goToMarket(market) { + window.location.href = `/markets/${market}`; +} \ No newline at end of file diff --git a/backend_copy/templates/static/mypage.css b/backend_copy/templates/static/mypage.css new file mode 100644 index 0000000..85800c5 --- /dev/null +++ b/backend_copy/templates/static/mypage.css @@ -0,0 +1,39 @@ +body { + width: 360px; + height: 772px; +} +header { + border-bottom: 3px solid black; + text-align: center; + font-size: 35px; + padding-left: 40px; + padding-bottom: 5px; +} + +#img_setting { + float: right; + width: 35px; + height: 35px; + margin-right: 5px; + margin-top: 5px; +} +main { + height: 605px; +} +img { + width: 50px; + height: 50px; +} + +footer { + display: flex; + border-top: 3px solid black; + text-align: center; + justify-content: space-evenly; + padding-top: 5px; +} + +footer img { + width: 40px; + height: 40px; +} diff --git a/backend_copy/templates/static/mypage.js b/backend_copy/templates/static/mypage.js new file mode 100644 index 0000000..e0a834f --- /dev/null +++ b/backend_copy/templates/static/mypage.js @@ -0,0 +1,4 @@ +function goTo(url) { + const encoded = encodeURIComponent(url); + window.location.href = `/backend_copy/templates/${encoded}.html`; +} \ No newline at end of file diff --git a/backend_copy/templates/static/setting.css b/backend_copy/templates/static/setting.css new file mode 100644 index 0000000..85800c5 --- /dev/null +++ b/backend_copy/templates/static/setting.css @@ -0,0 +1,39 @@ +body { + width: 360px; + height: 772px; +} +header { + border-bottom: 3px solid black; + text-align: center; + font-size: 35px; + padding-left: 40px; + padding-bottom: 5px; +} + +#img_setting { + float: right; + width: 35px; + height: 35px; + margin-right: 5px; + margin-top: 5px; +} +main { + height: 605px; +} +img { + width: 50px; + height: 50px; +} + +footer { + display: flex; + border-top: 3px solid black; + text-align: center; + justify-content: space-evenly; + padding-top: 5px; +} + +footer img { + width: 40px; + height: 40px; +} diff --git a/backend_copy/templates/static/setting.js b/backend_copy/templates/static/setting.js new file mode 100644 index 0000000..da2f05e --- /dev/null +++ b/backend_copy/templates/static/setting.js @@ -0,0 +1,5 @@ +function goTo(url) { + const encoded = encodeURIComponent(url); + window.location.href = `/backend_copy/templates/${encoded}.html`; + +} \ No newline at end of file diff --git a/backend_copy/templates/static/style.css b/backend_copy/templates/static/style.css new file mode 100644 index 0000000..30871dc --- /dev/null +++ b/backend_copy/templates/static/style.css @@ -0,0 +1,8 @@ +body { + font-family: Arial, sans-serif; +} + +h1 { + background-color: rgba(156, 156, 156, 0.281); + text-align: center; +} \ No newline at end of file diff --git a/backend_copy/templates/static/symbols_info.css b/backend_copy/templates/static/symbols_info.css new file mode 100644 index 0000000..b38feed --- /dev/null +++ b/backend_copy/templates/static/symbols_info.css @@ -0,0 +1,71 @@ +body { + width: 360px; + height: 772px; +} +header { + border-bottom: 3px solid black; + text-align: center; + font-size: 35px; + padding-left: 40px; + padding-bottom: 5px; +} + +#img_setting { + float: right; + width: 35px; + height: 35px; + margin-right: 5px; + margin-top: 5px; +} +main { + height: 605px; +} +img { + width: 50px; + height: 50px; +} + +footer { + display: flex; + border-top: 3px solid black; + text-align: center; + justify-content: space-evenly; + padding-top: 5px; +} + +footer img { + width: 40px; + height: 40px; +} + + +.crypto-table { + width: 100%; + border-collapse: collapse; + padding-top: 20px; +} +.crypto-table th, .crypto-table td { + border: 1px solid #ddd; + padding: 8px; +} +.crypto-table th { + background-color: #f2f2f2; + text-align: left; +} +.crypto-table td { + text-align: right; +} +.crypto-table th:first-child, .crypto-table td:first-child { + text-align: left; +} + +.positive { + color: red; +} +.negative { + color: blue; +} +.neutral { + color: black; +} + diff --git a/backend_copy/templates/static/symbols_info.js b/backend_copy/templates/static/symbols_info.js new file mode 100644 index 0000000..9e31518 --- /dev/null +++ b/backend_copy/templates/static/symbols_info.js @@ -0,0 +1,61 @@ +function goTo(url) { + const encoded = encodeURIComponent(url); + window.location.href = `/backend_copy/templates/${encoded}.html`; +} + + +document.addEventListener("DOMContentLoaded", function() { + const apiURL = "http://127.0.0.1:8000/symbols_info"; // FastAPI 서버 URL + + function fetchData() { + fetch(apiURL) + .then(response => response.json()) + .then(data => { + const tbody = document.getElementById("crypto-prices"); + tbody.innerHTML = ""; // Clear previous content + const cryptos = ["ETH", "BTC", "XRP", "DOGE", "SOL", "STX"]; + + cryptos.forEach(crypto => { + const row = document.createElement("tr"); + + const symbolCell = document.createElement("td"); + symbolCell.textContent = cryptoNames(crypto); + + const priceCell = document.createElement("td"); + priceCell.textContent = `${data[crypto + '_cur_price']}$`; + + const changeCell = document.createElement("td"); + const changeValue = data[crypto + '_daily_per']; + changeCell.textContent = `${changeValue}%`; + + if (changeValue > 0) { + changeCell.classList.add("positive"); + } else if (changeValue < 0) { + changeCell.classList.add("negative"); + } else { + changeCell.classList.add("neutral"); + } + + row.appendChild(symbolCell); + row.appendChild(priceCell); + row.appendChild(changeCell); + tbody.appendChild(row); + }); + }); + } + + function cryptoNames(symbol) { + const names = { + "ETH": "ETH", + "BTC": "BTC", + "XRP": "XRP", + "DOGE": "DOGE", + "SOL": "SOL", + "STX": "STX" + }; + return names[symbol]; + } + + fetchData(); // Initial fetch + setInterval(fetchData, 1000); // Fetch every 1 second +}); \ No newline at end of file diff --git a/backend_copy/templates/static/test.js b/backend_copy/templates/static/test.js new file mode 100644 index 0000000..216d7cb --- /dev/null +++ b/backend_copy/templates/static/test.js @@ -0,0 +1,28 @@ +document.addEventListener("DOMContentLoaded", function() { + document.getElementById("balance_form").addEventListener("submit", function(event) { + event.preventDefault(); + axios.post('/balance') + .then(function(response) { + document.getElementById("balance").innerHTML = response.data; + }) + .catch(function(error) { + console.error("There was an error retrieving the balance!", error); + }); + }); + + document.getElementById("curPrice_form").addEventListener("submit", function(event) { + event.preventDefault(); + const coinName = document.getElementById("symbol").value; + const formData = new FormData(); + formData.append("symbol", coinName); + + axios.post('/cur_price', formData) + .then(function(response) { + document.getElementById("cur_price").innerHTML = response.data; + }) + .catch(function(error) { + console.error("There was an error retrieving the current price!", error); + }); + }); + +}); diff --git a/backend_copy/templates/symbols_info.html b/backend_copy/templates/symbols_info.html new file mode 100644 index 0000000..f509879 --- /dev/null +++ b/backend_copy/templates/symbols_info.html @@ -0,0 +1,47 @@ + + + + 코인 전략 선택 + + + + + + +
+ Bit4Coin + +
+
+
+ + + + + + + + + + + +
SymbolsCurrent Price시가대비
+
+
+ + + + + + +