Introduce Order database model

This commit is contained in:
Matthias 2020-08-13 09:34:53 +02:00
parent 7d03a067ee
commit 171a52b21a
4 changed files with 74 additions and 6 deletions

View File

@ -487,6 +487,7 @@ class Exchange:
'side': side, 'side': side,
'remaining': _amount, 'remaining': _amount,
'datetime': arrow.utcnow().isoformat(), 'datetime': arrow.utcnow().isoformat(),
'timestamp': arrow.utcnow().timestamp,
'status': "closed" if ordertype == "market" else "open", 'status': "closed" if ordertype == "market" else "open",
'fee': None, 'fee': None,
'info': {} 'info': {}

View File

@ -22,7 +22,7 @@ from freqtrade.exceptions import (DependencyException, ExchangeError,
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date
from freqtrade.misc import safe_value_fallback, safe_value_fallback2 from freqtrade.misc import safe_value_fallback, safe_value_fallback2
from freqtrade.pairlist.pairlistmanager import PairListManager from freqtrade.pairlist.pairlistmanager import PairListManager
from freqtrade.persistence import Trade from freqtrade.persistence import Order, Trade
from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.resolvers import ExchangeResolver, StrategyResolver
from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.rpc import RPCManager, RPCMessageType
from freqtrade.state import State from freqtrade.state import State
@ -527,6 +527,7 @@ class FreqtradeBot:
order = self.exchange.buy(pair=pair, ordertype=order_type, order = self.exchange.buy(pair=pair, ordertype=order_type,
amount=amount, rate=buy_limit_requested, amount=amount, rate=buy_limit_requested,
time_in_force=time_in_force) time_in_force=time_in_force)
order_obj = Order.parse_from_ccxt_object(order, pair)
order_id = order['id'] order_id = order['id']
order_status = order.get('status', None) order_status = order.get('status', None)
@ -580,6 +581,7 @@ class FreqtradeBot:
strategy=self.strategy.get_strategy_name(), strategy=self.strategy.get_strategy_name(),
timeframe=timeframe_to_minutes(self.config['timeframe']) timeframe=timeframe_to_minutes(self.config['timeframe'])
) )
trade.orders.append(order_obj)
# Update fees if order is closed # Update fees if order is closed
if order_status == 'closed': if order_status == 'closed':
@ -781,6 +783,9 @@ class FreqtradeBot:
stoploss_order = self.exchange.stoploss(pair=trade.pair, amount=trade.amount, stoploss_order = self.exchange.stoploss(pair=trade.pair, amount=trade.amount,
stop_price=stop_price, stop_price=stop_price,
order_types=self.strategy.order_types) order_types=self.strategy.order_types)
order_obj = Order.parse_from_ccxt_object(stoploss_order, trade.pair)
trade.orders.append(order_obj)
trade.stoploss_order_id = str(stoploss_order['id']) trade.stoploss_order_id = str(stoploss_order['id'])
return True return True
except InvalidOrderException as e: except InvalidOrderException as e:
@ -1123,12 +1128,15 @@ class FreqtradeBot:
return False return False
# Execute sell and update trade record # Execute sell and update trade record
order = self.exchange.sell(pair=str(trade.pair), order = self.exchange.sell(pair=trade.pair,
ordertype=order_type, ordertype=order_type,
amount=amount, rate=limit, amount=amount, rate=limit,
time_in_force=time_in_force time_in_force=time_in_force
) )
order_obj = Order.parse_from_ccxt_object(order, trade.pair)
trade.orders.append(order_obj)
trade.open_order_id = order['id'] trade.open_order_id = order['id']
trade.close_rate_requested = limit trade.close_rate_requested = limit
trade.sell_reason = sell_reason.value trade.sell_reason = sell_reason.value

View File

@ -1,3 +1,4 @@
# flake8: noqa: F401 # flake8: noqa: F401
from freqtrade.persistence.models import Trade, clean_dry_run_db, cleanup, init from freqtrade.persistence.models import (Order, Trade, clean_dry_run_db,
cleanup, init)

View File

@ -7,11 +7,11 @@ from decimal import Decimal
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
import arrow import arrow
from sqlalchemy import (Boolean, Column, DateTime, Float, Integer, String, from sqlalchemy import (Boolean, Column, DateTime, Float, Integer, String, ForeignKey,
create_engine, desc, func) create_engine, desc, func)
from sqlalchemy.exc import NoSuchModuleError from sqlalchemy.exc import NoSuchModuleError
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Query from sqlalchemy.orm import Query, relationship
from sqlalchemy.orm.scoping import scoped_session from sqlalchemy.orm.scoping import scoped_session
from sqlalchemy.orm.session import sessionmaker from sqlalchemy.orm.session import sessionmaker
from sqlalchemy.pool import StaticPool from sqlalchemy.pool import StaticPool
@ -85,13 +85,71 @@ def clean_dry_run_db() -> None:
trade.open_order_id = None trade.open_order_id = None
class Order(_DECL_BASE):
"""
Order database model
Keeps a record of all orders placed on the exchange
One to many relationship with Trades:
- One trade can have many orders
- One Order can only be associated with one Trade
Mirrors CCXT Order structure
"""
__tablename__ = 'orders'
id = Column(Integer, primary_key=True)
trade_id = Column(Integer, ForeignKey('trades.id'), index=True)
order_id = Column(String, nullable=False, index=True)
status = Column(String, nullable=False)
symbol = Column(String, nullable=False)
order_type = Column(String, nullable=False)
side = Column(String, nullable=False)
price = Column(Float, nullable=False)
amount = Column(Float, nullable=False)
filled = Column(Float, nullable=True)
remaining = Column(Float, nullable=True)
cost = Column(Float, nullable=True)
order_date = Column(DateTime, nullable=False, default=datetime.utcnow)
order_filled_date = Column(DateTime, nullable=True)
@staticmethod
def parse_from_ccxt_object(order, pair) -> 'Order':
"""
Parse an order from a ccxt object and return a new order Object.
"""
o = Order(order_id=str(order['id']))
o.status = order['status']
o.symbol = order.get('symbol', pair)
o.order_type = order['type']
o.side = order['side']
o.price = order['price']
o.amount = order['amount']
o.filled = order.get('filled')
o.remaining = order.get('remaining')
o.cost = order.get('cost')
o.order_date = datetime.fromtimestamp(order['timestamp'])
return o
def __repr__(self):
return (f'Order(id={self.id}, trade_id={self.trade_id}, side={self.side}, '
f'status={self.status})')
class Trade(_DECL_BASE): class Trade(_DECL_BASE):
""" """
Class used to define a trade structure Trade database model.
Also handles updating and querying trades
""" """
__tablename__ = 'trades' __tablename__ = 'trades'
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
orders = relationship("Order", order_by="Order.id")
exchange = Column(String, nullable=False) exchange = Column(String, nullable=False)
pair = Column(String, nullable=False, index=True) pair = Column(String, nullable=False, index=True)
is_open = Column(Boolean, nullable=False, default=True, index=True) is_open = Column(Boolean, nullable=False, default=True, index=True)