Skip to content

Commit

Permalink
Buncha random stuff. Random!
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbenincasa committed Jan 29, 2022
1 parent b2830df commit 71cd803
Show file tree
Hide file tree
Showing 13 changed files with 1,108 additions and 54 deletions.
24 changes: 24 additions & 0 deletions app/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';

import counterReducer from '../features/counterSlice';

export function makeStore() {
return configureStore({
reducer: { counter: counterReducer },
});
}

const store = makeStore();

export type AppState = ReturnType<typeof store.getState>;

export type AppDispatch = typeof store.dispatch;

export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
AppState,
unknown,
Action<string>
>;

export default store;
113 changes: 107 additions & 6 deletions components/tradesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { toFormat } from 'dinero.js';
import { Dinero, toFormat, add, toUnit } from 'dinero.js';
import _ from 'lodash';
import { DateTime } from 'luxon';
import { useMemo } from 'react';
import { Column, useTable } from 'react-table';
import {
Area,
AreaChart,
CartesianGrid,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import { Trade } from '../model';
import { dineroFromSnapshot } from '../util/dineroUtil';
import { dineroFromSnapshot, zero } from '../util/dineroUtil';
import { groupByDate } from '../util/tradeUtil';

interface Props {
trades: Trade[];
Expand Down Expand Up @@ -51,24 +62,78 @@ export default function TradesTable(props: Props) {
timeOpened: trade.executions[0].timestamp.toISO(),
longOrShort: trade.isShort ? 'Short' : 'Long',
realizedPL: toFormat(
dineroFromSnapshot(trade.closedPl),
trade.closedPl,
({ amount, currency }) => `$${amount} ${currency.code}`
),
} as TableData;
});
}, [props.trades]);

const fmtMoney = (d: Dinero<number>) => {
return toFormat(d, ({ amount, currency }) => `$${amount} ${currency.code}`);
};

const calcPL = (trades: Trade[]): Dinero<number> => {
if (trades.length === 0) {
return zero();
}

const start = trades[0].closedPl;

return _.chain(trades)
.drop(1)
.reduce((acc, trade) => {
return add(start, trade.closedPl);
}, start)
.value();
};

const chartData = useMemo(() => {
if (props.trades.length === 0) {
console.log('em');
return [];
}

const x = _.map(props.trades, (trade) => {
return [trade.executions[0].timestamp.startOf('day'), trade];
});

const byDate = groupByDate(props.trades);

console.log(byDate, 'hel');
const cumPl: { name: DateTime; y: Dinero<number> }[] = [];
for (let i = 0; i < byDate.length; i++) {
const [date, trades] = byDate[i];
let val;
if (i === 0) {
val = calcPL(trades);
} else {
val = add(cumPl[i - 1].y, calcPL(trades));
}
cumPl.push({ name: date, y: val });
}

return _.map(cumPl, (datum) => ({
name: datum.name.toFormat('yyyy-MM-dd'),
y: toUnit(datum.y),
}));
}, [props.trades]);

const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
useTable({ columns, data });

return (
<div className="overflow-hidden rounded-t-xl bg-gradient-to-b from-slate-100 p-10">
<div
className="overflow-hidden
rounded-t-xl bg-gradient-to-b from-slate-100 p-10"
>
<table {...getTableProps()} className="table-fixed">
<thead>
{headerGroups.map((headerGroup) => (
<tr
{...headerGroup.getHeaderGroupProps()}
className="border-b border-slate-100 p-4 pl-8 text-slate-500 dark:border-slate-700 dark:text-slate-400"
className="border-b border-slate-100 p-4 pl-8
text-slate-500 dark:border-slate-700 dark:text-slate-400"
>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
Expand All @@ -82,7 +147,8 @@ export default function TradesTable(props: Props) {
return (
<tr
{...row.getRowProps()}
className="border-b border-slate-100 p-4 pl-8 text-slate-500 dark:border-slate-700 dark:text-slate-400"
className="border-b border-slate-100 p-4 pl-8
text-slate-500 dark:border-slate-700 dark:text-slate-400"
>
{row.cells.map((cell) => {
return (
Expand All @@ -94,6 +160,41 @@ export default function TradesTable(props: Props) {
})}
</tbody>
</table>
<AreaChart
width={730}
height={250}
data={chartData}
margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
>
<defs>
<linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#8884d8" stopOpacity={0.8} />
<stop offset="95%" stopColor="#8884d8" stopOpacity={0} />
</linearGradient>
<linearGradient id="colorPv" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#82ca9d" stopOpacity={0.8} />
<stop offset="95%" stopColor="#82ca9d" stopOpacity={0} />
</linearGradient>
</defs>
<XAxis dataKey="name" />
<YAxis />
<CartesianGrid strokeDasharray="3 3" />
<Tooltip />
<Area
type="monotone"
dataKey="y"
stroke="#8884d8"
fillOpacity={1}
fill="url(#colorUv)"
/>
{/* <Area
type="monotone"
dataKey="pv"
stroke="#82ca9d"
fillOpacity={1}
fill="url(#colorPv)"
/> */}
</AreaChart>
</div>
);
}
84 changes: 84 additions & 0 deletions features/counterSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';

import type { AppState, AppThunk } from '../app/store';

export interface CounterState {
value: number;
status: 'idle' | 'loading' | 'failed';
}

const initialState: CounterState = {
value: 0,
status: 'idle',
};

// The function below is called a thunk and allows us to perform async logic. It
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
// will call the thunk with the `dispatch` function as the first argument. Async
// code can then be executed and other actions can be dispatched. Thunks are
// typically used to make async requests.
export const incrementAsync = createAsyncThunk(
'counter/fetchCount',
(amount: number) => {
const response = { data: 1 };
// The value we return becomes the `fulfilled` action payload
return response.data;
}
);

export const counterSlice = createSlice({
name: 'counter',
initialState,
// The `reducers` field lets us define reducers and generate associated
// actions
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
// Use the PayloadAction type to declare the contents of `action.payload`
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
// The `extraReducers` field lets the slice handle actions defined elsewhere,
// including actions generated by createAsyncThunk or in other slices.
extraReducers: (builder) => {
builder
.addCase(incrementAsync.pending, (state) => {
state.status = 'loading';
})
.addCase(incrementAsync.fulfilled, (state, action) => {
state.status = 'idle';
state.value += action.payload;
});
},
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;

// The function below is called a selector and allows us to select a value from
// the state. Selectors can also be defined inline where they're used instead of
// in the slice file. For example:
// `useSelector((state: RootState) => state.counter.value)`
export const selectCount = (state: AppState) => state.counter.value;

// We can also write thunks by hand, which may contain both sync and async
// logic. Here's an example of conditionally dispatching actions based on
// current state.
export const incrementIfOdd = (amount: number): AppThunk => {
return (dispatch, getState) => {
const currentValue = selectCount(getState());
if (currentValue % 2 === 1) {
dispatch(incrementByAmount(amount));
}
};
};

export default counterSlice.reducer;
35 changes: 27 additions & 8 deletions model/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
import { Dinero, DineroSnapshot } from 'dinero.js';
import { DateTime } from 'luxon';

export enum ExecutionType {
BUY_TO_OPEN,
SELL_TO_OPEN,
BUY_TO_CLOSE,
SELL_TO_CLOSE,
}

export interface ExecutionJson {
symbol: string;
executionType: ExecutionType;
timestamp: DateTime;
quantity: number;
pps: DineroSnapshot<number>;
totalOutflow: DineroSnapshot<number>;
}

export interface TradeJson {
symbol: string;
quantity: number;
executions: ExecutionJson[];
isOpen: boolean;
isShort: boolean;
closedPl: DineroSnapshot<number>;
outflow: DineroSnapshot<number>;
}

export interface Execution {
symbol: string;
executionType: ExecutionType;
Expand All @@ -10,19 +36,12 @@ export interface Execution {
totalOutflow: Dinero<number>;
}

export enum ExecutionType {
BUY_TO_OPEN,
SELL_TO_OPEN,
BUY_TO_CLOSE,
SELL_TO_CLOSE,
}

export interface Trade {
symbol: string;
quantity: number;
executions: Execution[];
isOpen: boolean;
isShort: boolean;
closedPl: DineroSnapshot<number>;
closedPl: Dinero<number>;
outflow: Dinero<number>;
}
Loading

0 comments on commit 71cd803

Please sign in to comment.